github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/range.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "math" 6 "regexp" 7 "strconv" 8 "strings" 9 "unicode" 10 11 consts "github.com/easysoft/zendata/internal/pkg/const" 12 "github.com/easysoft/zendata/internal/pkg/domain" 13 "github.com/easysoft/zendata/internal/pkg/helper" 14 commonUtils "github.com/easysoft/zendata/pkg/utils/common" 15 fileUtils "github.com/easysoft/zendata/pkg/utils/file" 16 stringUtils "github.com/easysoft/zendata/pkg/utils/string" 17 "github.com/easysoft/zendata/pkg/utils/vari" 18 ) 19 20 type RangeService struct { 21 PlaceholderService *PlaceholderService `inject:""` 22 ListService *ListService `inject:""` 23 RandomService *RandomService `inject:""` 24 25 DefService *DefService `inject:""` 26 PrintService *PrintService `inject:""` 27 CombineService *CombineService `inject:""` 28 OutputService *OutputService `inject:""` 29 FileService *FileService `inject:""` 30 31 RangeService *RangeService `inject:""` 32 MainService *MainService `inject:""` 33 } 34 35 func (s *RangeService) CreateFieldValuesFromRange(field *domain.DefField) { 36 rang := field.Range 37 rangLiteral := field.RangeLiteral 38 39 if rangLiteral != "" { 40 field.Values = append(field.Values, rangLiteral) 41 return 42 } 43 44 // gen empty values 45 if rang == "" { 46 regx := regexp.MustCompile(`.+\(.*\)`) 47 isFunc := regx.MatchString(field.Format) 48 49 for i := 0; i < vari.GlobalVars.Total; i++ { 50 field.Values = append(field.Values, "") 51 52 if !isFunc { // gen multi records for format function 53 break 54 } 55 } 56 return 57 } 58 59 field.IsNumb = true 60 61 // gen from field's range 62 rangeSections := s.ParseRangeProperty(rang) // parse 1 63 64 index := 0 65 for _, rangeSection := range rangeSections { 66 if index >= consts.MaxNumb { 67 break 68 } 69 if rangeSection == "" { 70 continue 71 } 72 73 descStr, stepStr, count, countTag := s.ParseRangeSection(rangeSection) // parse 2 74 if strings.ToLower(stepStr) == "r" { 75 (*field).IsRand = true 76 } 77 78 typ, desc := s.ParseRangeSectionDesc(descStr) // parse 3 79 80 items := make([]interface{}, 0) 81 if typ == "interval" { 82 items = s.CreateValuesFromInterval(field, desc, stepStr, count, countTag) 83 84 } else if typ == "literal" { 85 items = s.CreateValuesFromLiteral(field, desc, stepStr, count, countTag) 86 87 } else if typ == "yaml" { // load from a yaml 88 items = s.CreateValuesFromYaml(field, desc, stepStr, count, countTag) 89 field.ReferToAnotherYaml = true 90 } 91 92 for _, item := range items { 93 field.Values = append(field.Values, item) 94 } 95 96 index = index + len(items) 97 } 98 99 if len(field.Values) == 0 { 100 field.Values = append(field.Values, "N/A") 101 } 102 } 103 104 func (s *RangeService) CreateValuesFromLiteral(field *domain.DefField, desc string, stepStr string, repeat int, repeatTag string) (items []interface{}) { 105 field.IsNumb = false 106 107 elemArr := s.ParseDesc(desc) 108 step, _ := strconv.Atoi(stepStr) 109 if step == 0 { 110 step = 1 111 } 112 total := 0 113 114 if field.Path != "" && stepStr == "r" { 115 pth := field.Path 116 key := s.GetRandFieldSection(pth) 117 118 items = append(items, s.PlaceholderService.PlaceholderStr(key)) 119 mp := s.PlaceholderService.PlaceholderMapForRandValues("list", elemArr, "", "", "", "", 120 field.Format, repeat, repeatTag) 121 122 vari.GlobalVars.RandFieldSectionShortKeysToPathMap[key] = pth 123 vari.GlobalVars.RandFieldSectionPathToValuesMap[key] = mp 124 return 125 } 126 127 if repeatTag == "" { 128 for i := 0; i < len(elemArr); { 129 idx := i 130 if field.Path == "" && stepStr == "r" { 131 idx = commonUtils.RandNum(len(elemArr)) // should set random here too 132 } 133 134 val := elemArr[idx] 135 val = strings.Trim(val, "`") 136 total = s.ListService.AppendValues(&items, val, repeat, total) 137 138 if total >= consts.MaxNumb { 139 break 140 } 141 i += step 142 } 143 } else if repeatTag == "!" { 144 isRand := field.Path == "" && stepStr == "r" 145 for i := 0; i < repeat; { 146 total = s.ListService.AppendArrItems(&items, elemArr, total, isRand) 147 148 if total >= consts.MaxNumb { 149 break 150 } 151 i += step 152 } 153 } 154 155 if field.Path == "" && stepStr == "r" { // for ranges and instances, random 156 items = s.RandomService.RandomInterfaces(items) 157 } 158 159 return 160 } 161 162 func (s *RangeService) CreateValuesFromInterval(field *domain.DefField, desc, stepStr string, repeat int, repeatTag string) (items []interface{}) { 163 elemArr := strings.Split(desc, "-") 164 startStr := elemArr[0] 165 endStr := startStr 166 if len(elemArr) > 1 { 167 endStr = elemArr[1] 168 } 169 170 dataType, step, precision, rand, _ := s.CheckRangeType(startStr, endStr, stepStr) 171 field.Precision = precision 172 173 // 1. random replacement 174 if field.Path != "" && dataType != "string" && rand { // random. for res, field.Path == "" 175 pth := field.Path + "->" + desc 176 key := s.GetRandFieldSection(pth) 177 178 val := s.PlaceholderService.PlaceholderStr(key) 179 strItems := make([]string, 0) 180 181 // chang to add only one placeholder item 182 items = append(items, val) 183 strItems = append(strItems, val) 184 185 mp := s.PlaceholderService.PlaceholderMapForRandValues(dataType, strItems, startStr, endStr, fmt.Sprintf("%v", step), 186 strconv.Itoa(precision), field.Format, repeat, repeatTag) 187 188 vari.GlobalVars.RandFieldSectionShortKeysToPathMap[key] = pth 189 vari.GlobalVars.RandFieldSectionPathToValuesMap[key] = mp 190 191 return 192 } 193 194 if dataType == "int" { 195 startInt, _ := strconv.ParseInt(startStr, 0, 64) 196 endInt, _ := strconv.ParseInt(endStr, 0, 64) 197 198 items = helper.GenerateItems(startInt, endInt, int64(step.(int)), 0, rand, repeat, repeatTag, 0) 199 200 } else if dataType == "float" { 201 startFloat, _ := strconv.ParseFloat(startStr, 64) 202 endFloat, _ := strconv.ParseFloat(endStr, 64) 203 field.Precision = precision 204 205 items = helper.GenerateItems(startFloat, endFloat, step.(float64), field.Precision, rand, repeat, repeatTag, 0) 206 207 } else if dataType == "char" { 208 field.IsNumb = false 209 210 items = helper.GenerateItems(startStr[0], endStr[0], int64(step.(int)), 0, rand, repeat, repeatTag, 0) 211 212 } else if dataType == "string" { 213 field.IsNumb = false 214 215 if repeat == 0 { 216 repeat = 1 217 } 218 for i := 0; i < repeat; i++ { 219 items = append(items, desc) 220 } 221 } 222 223 if field.Path == "" && stepStr == "r" { // for ranges and instances, random again 224 items = s.RandomService.RandomInterfaces(items) 225 } 226 227 return 228 } 229 230 func (s *RangeService) CreateValuesFromYaml(field *domain.DefField, yamlFile, stepStr string, repeat int, repeatTag string) (items []interface{}) { 231 field.IsNumb = false 232 233 // keep root def, since vari.ZdDef will be overwrite by refer yaml file 234 rootDef := vari.GlobalVars.DefData 235 configDir := vari.GlobalVars.ConfigFileDir 236 exportFields := vari.GlobalVars.ExportFields 237 res := vari.GlobalVars.ResData 238 239 vari.GlobalVars.ExportFields = make([]string, 0) // set to empty to use all fields 240 241 configFile := s.FileService.ComputerReferFilePath(yamlFile, field) 242 243 vari.GlobalVars.ConfigFileDir = fileUtils.GetAbsDir(configFile) 244 s.MainService.GenerateDataByFile([]string{configFile}) 245 246 // get the data 247 items = s.OutputService.GenText(true) 248 249 if field.Rand { 250 items = s.RandomService.RandomValues(items) 251 } 252 253 if repeat > 0 { 254 if repeat > len(items) { 255 repeat = len(items) 256 } 257 items = items[:repeat] 258 } 259 260 // rollback root def when finish to deal with refer yaml file 261 vari.GlobalVars.DefData = rootDef 262 vari.GlobalVars.ConfigFileDir = configDir 263 vari.GlobalVars.ExportFields = exportFields 264 vari.GlobalVars.ResData = res 265 266 return 267 } 268 269 func (s *RangeService) DealwithFixRange(field *domain.DefField) { 270 if s.FixIsARange(field.Prefix) { 271 field.PrefixRange = s.CreateAffixValuesFromRange(field.Prefix, field) 272 } else { 273 var tmp interface{} 274 tmp = field.Prefix 275 field.PrefixRange = &domain.Range{Values: []interface{}{tmp}} 276 } 277 278 if s.FixIsARange(field.Postfix) { 279 field.PostfixRange = s.CreateAffixValuesFromRange(field.Postfix, field) 280 } else { 281 var tmp interface{} 282 tmp = field.Postfix 283 field.PostfixRange = &domain.Range{Values: []interface{}{tmp}} 284 } 285 } 286 287 func (s *RangeService) CreateAffixValuesFromRange(strRang string, field *domain.DefField) (rang *domain.Range) { 288 rang = &domain.Range{} 289 290 if strRang == "" { 291 return 292 } 293 294 rangeSections := s.ParseRangeProperty(strRang) // parse 1 295 296 index := 0 297 for _, rangeSection := range rangeSections { 298 if index >= vari.GlobalVars.Total { 299 break 300 } 301 302 if rangeSection == "" { 303 continue 304 } 305 306 descStr, stepStr, count, countTag := s.ParseRangeSection(rangeSection) // parse 2 307 if strings.ToLower(stepStr) == "r" { 308 rang.IsRand = true 309 } 310 311 typ, desc := s.ParseRangeSectionDesc(descStr) // parse 3 312 313 items := make([]interface{}, 0) 314 if typ == "literal" { 315 items = s.CreateValuesFromLiteral(field, desc, stepStr, count, countTag) 316 317 } else if typ == "interval" { 318 items = s.CreateValuesFromInterval(field, desc, stepStr, count, countTag) 319 320 } else if typ == "yaml" { // load from a yaml 321 items = s.CreateValuesFromYaml(field, desc, stepStr, count, countTag) 322 field.ReferToAnotherYaml = true 323 324 } 325 326 rang.Values = append(rang.Values, items...) 327 index = index + len(items) 328 } 329 330 if len(rang.Values) == 0 { 331 rang.Values = append(rang.Values, "N/A") 332 } 333 334 return 335 } 336 337 func (s *RangeService) FixIsARange(fix string) bool { 338 index := strings.Index(fix, "-") 339 340 return index > 0 && index < len(fix)-1 341 } 342 343 func (s *RangeService) ParseRangeProperty(rang string) []string { 344 items := make([]string, 0) 345 346 bracketsOpen := false 347 backtickOpen := false 348 temp := "" 349 350 rang = strings.Trim(rang, ",") 351 runeArr := []rune(rang) 352 353 for i := 0; i < len(runeArr); i++ { 354 c := runeArr[i] 355 356 if c == consts.RightBrackets { 357 bracketsOpen = false 358 } else if c == consts.LeftBrackets { 359 bracketsOpen = true 360 } else if !backtickOpen && c == consts.Backtick { 361 backtickOpen = true 362 } else if backtickOpen && c == consts.Backtick { 363 backtickOpen = false 364 } 365 366 if i == len(runeArr)-1 { 367 temp += fmt.Sprintf("%c", c) 368 items = append(items, temp) 369 } else if !bracketsOpen && !backtickOpen && c == ',' { 370 items = append(items, temp) 371 temp = "" 372 bracketsOpen = false 373 backtickOpen = false 374 } else { 375 temp += fmt.Sprintf("%c", c) 376 } 377 } 378 379 return items 380 } 381 382 // for Literal only 383 func (s *RangeService) ParseDesc(desc string) (items []string) { 384 desc = strings.TrimSpace(desc) 385 desc = strings.Trim(desc, ",") 386 387 if desc == "" { 388 items = append(items, desc) 389 return 390 } 391 392 runeArr := []rune(desc) 393 394 if runeArr[0] == consts.Backtick && runeArr[len(runeArr)-1] == consts.Backtick { // `xxx` 395 desc = string(runeArr[1 : len(runeArr)-1]) 396 items = append(items, desc) 397 398 } else if runeArr[0] == consts.LeftBrackets && runeArr[len(runeArr)-1] == consts.RightBrackets { // [abc,123] 399 desc = string(runeArr[1 : len(runeArr)-1]) 400 items = strings.Split(desc, ",") 401 402 } else { 403 items = append(items, desc) 404 } 405 406 return 407 } 408 409 /* 410 * 411 412 convert range item to entity, step, repeat 413 [user1,user2]{2} -> entry =>[user1,user2] 414 step =>1 415 repeat =>2 416 */ 417 func (s *RangeService) ParseRangeSection(rang string) (entry string, step string, repeat int, repeatTag string) { 418 rang = strings.TrimSpace(rang) 419 420 if rang == "" { 421 repeat = 1 422 return 423 } 424 425 runeArr := []rune(rang) 426 if (runeArr[0] == consts.Backtick && runeArr[len(runeArr)-1] == consts.Backtick) || // `xxx` 427 (string(runeArr[0]) == string(consts.LeftBrackets) && // (xxx) 428 string(runeArr[len(runeArr)-1]) == string(consts.RightBrackets)) { 429 430 entry = rang 431 if repeat == 0 { 432 repeat = 1 433 } 434 return 435 } 436 437 repeat, repeatTag, rangWithoutRepeat := s.ParseRepeat(rang) 438 439 sectionArr := strings.Split(rangWithoutRepeat, ":") 440 entry = sectionArr[0] 441 if len(sectionArr) == 2 { 442 step = strings.TrimSpace(strings.ToLower(sectionArr[1])) 443 } 444 445 if step != "" { 446 pattern := "\\d+" 447 isNum, _ := regexp.MatchString(pattern, step) 448 if !isNum && step != "r" { 449 entry = rang 450 step = "" 451 } 452 } 453 454 if repeat == 0 { 455 repeat = 1 456 } 457 return entry, step, repeat, repeatTag 458 } 459 460 /* 461 * 462 463 get range item entity's type and desc 464 1-9 or [1-9] -> type => interval 465 desc => 1-9 or [1-9] 466 [user1,user2] -> type => literal 467 desc => user2,user3 468 */ 469 func (s *RangeService) ParseRangeSectionDesc(str string) (typ string, desc string) { 470 desc = strings.TrimSpace(str) 471 runeArr := []rune(desc) 472 473 if desc == "" { 474 typ = "literal" 475 return 476 } 477 478 if stringUtils.EndWith(desc, ".yaml") { // refer to another yaml file 479 typ = "yaml" 480 return 481 } 482 483 if string(runeArr[0]) == string(consts.LeftBrackets) && // [a-z,1-9,userA,UserB] 484 string(runeArr[len(runeArr)-1]) == string(consts.RightBrackets) { 485 486 desc = s.removeBoundary(desc) 487 arr := strings.Split(desc, ",") 488 489 temp := "" 490 for _, item := range arr { 491 if isScopeStr(item) && s.isCharOrNumberScope(item) { // only support a-z and 0-9 in [] 492 tempField := domain.DefField{} 493 values := s.CreateValuesFromInterval(&tempField, item, "", 1, "") 494 495 for _, val := range values { 496 temp += helper.InterfaceToStr(val) + "," 497 } 498 } else { 499 temp += item + "," 500 } 501 } 502 503 temp = strings.TrimSuffix(temp, ",") 504 desc = string(consts.LeftBrackets) + temp + string(consts.RightBrackets) 505 typ = "literal" 506 507 return 508 } 509 510 if strings.Contains(desc, ",") || strings.Contains(desc, "`") || !strings.Contains(desc, "-") { 511 typ = "literal" 512 } else { 513 temp := s.removeBoundary(desc) 514 515 if isScopeStr(temp) { 516 typ = "interval" 517 desc = temp 518 } else { 519 typ = "literal" 520 } 521 } 522 523 return 524 } 525 526 func (s *RangeService) removeBoundary(str string) string { 527 str = strings.TrimLeft(str, string(consts.LeftBrackets)) 528 str = strings.TrimRight(str, string(consts.RightBrackets)) 529 530 return str 531 } 532 533 func isScopeStr(str string) bool { 534 arr := strings.Split(str, "-") 535 if len(arr) < 2 || strings.TrimSpace(str) == "-" || arr[0] == "" || arr[len(arr)-1] == "" { 536 return false 537 } 538 539 left := strings.TrimSpace(arr[0]) 540 right := strings.TrimSpace(arr[1]) 541 542 if len(left) != 1 || len(right) != 1 { // more than on char, must be number 543 leftRune := []rune(string(left[0]))[0] 544 rightRune := []rune(string(right[0]))[0] 545 546 if unicode.IsNumber(leftRune) && unicode.IsNumber(rightRune) { 547 return true 548 } else { 549 return false 550 } 551 } else { 552 leftRune := []rune(string(left[0]))[0] 553 rightRune := []rune(string(right[0]))[0] 554 555 if (unicode.IsLetter(leftRune) && unicode.IsLetter(rightRune)) || 556 (unicode.IsNumber(leftRune) && unicode.IsNumber(rightRune)) { 557 return true 558 } else { 559 return false 560 } 561 } 562 } 563 564 func (s *RangeService) isCharOrNumberScope(str string) bool { 565 arr := strings.Split(str, "-") 566 if len(arr) < 2 { 567 return false 568 } 569 570 left := strings.TrimSpace(arr[0]) 571 right := strings.TrimSpace(arr[1]) 572 573 if len(left) == 1 && len(right) == 1 { 574 return true 575 } 576 577 return false 578 } 579 580 func (s *RangeService) ParseRepeat(rang string) (repeat int, repeatTag, rangeWithoutRepeat string) { 581 repeat = 1 582 583 regx := regexp.MustCompile(`\{(.*)!?\}`) 584 arr := regx.FindStringSubmatch(rang) 585 tag := "" 586 if len(arr) == 2 { 587 str := strings.TrimSpace(arr[1]) 588 if str[len(str)-1:] == "!" { 589 tag = str[len(str)-1:] 590 str = strings.TrimSpace(str[:len(str)-1]) 591 } 592 repeat, _ = strconv.Atoi(str) 593 } 594 repeatTag = tag 595 rangeWithoutRepeat = regx.ReplaceAllString(rang, "") 596 597 return 598 } 599 600 func (s *RangeService) CheckRangeType(startStr string, endStr string, stepStr string) (dataType string, step interface{}, precision int, 601 rand bool, count int) { 602 603 stepStr = strings.ToLower(strings.TrimSpace(stepStr)) 604 605 if start, end, stepi, ok := s.checkRangeTypeIsInt(startStr, endStr, stepStr); ok { // is int 606 step = 1 607 if stepStr == "r" { 608 rand = true 609 } 610 611 count = (int)(start-end) / int(stepi) 612 if count < 0 { 613 count = count * -1 614 } 615 616 dataType = "int" 617 step = int(stepi) 618 return 619 } else if start, end, stepf, ok := s.checkRangeTypeIsFloat(startStr, endStr, stepStr); ok { // is float 620 step = stepf 621 622 if stepStr == "r" { 623 rand = true 624 } 625 626 precision1, step1 := helper.GetPrecision(start, step) 627 precision2, step2 := helper.GetPrecision(end, step) 628 if precision1 < precision2 { 629 precision = precision2 630 step = step2 631 } else { 632 precision = precision1 633 step = step1 634 } 635 636 if (start > end && stepf > 0) || (start < end && stepf < 0) { 637 step = -1 * stepf 638 } 639 640 dataType = "float" 641 count = int(math.Floor(math.Abs(start-end)/stepf + 0.5)) 642 return 643 644 } else if len(startStr) == 1 && len(endStr) == 1 { // is char 645 step = 1 646 647 if stepStr != "r" { 648 stepChar, errChar3 := strconv.Atoi(stepStr) 649 if errChar3 == nil { 650 step = stepChar 651 } 652 } else if stepStr == "r" { 653 rand = true 654 } 655 656 if (strings.Compare(startStr, endStr) > 0 && step.(int) > 0) || 657 (strings.Compare(startStr, endStr) < 0 && step.(int) < 0) { 658 step = -1 * step.(int) 659 } 660 661 dataType = "char" 662 663 startChar := startStr[0] 664 endChar := endStr[0] 665 666 gap := 0 667 if endChar > startChar { // 负数转uint单独处理 668 gap = int(endChar - startChar) 669 } else { 670 gap = int(startChar - endChar) 671 } 672 count = gap / step.(int) 673 if count < 0 { 674 count = count * -1 675 } 676 return 677 } 678 679 // else is string 680 dataType = "string" 681 step = 1 682 return 683 } 684 685 func (s *RangeService) checkRangeTypeIsInt(startStr string, endStr string, stepStr string) ( 686 start int64, end int64, step int64, ok bool) { 687 step = 1 688 689 stepStr = strings.ToLower(strings.TrimSpace(stepStr)) 690 691 start, errInt1 := strconv.ParseInt(startStr, 0, 64) 692 end, errInt2 := strconv.ParseInt(endStr, 0, 64) 693 var errInt3 error 694 695 if stepStr != "" && stepStr != "r" { 696 step, errInt3 = strconv.ParseInt(stepStr, 0, 64) 697 } 698 699 if errInt1 == nil && errInt2 == nil && errInt3 == nil { // is int 700 if (start > end && step > 0) || (start < end && step < 0) { 701 step = -1 * step 702 } 703 704 ok = true 705 return 706 707 } 708 709 return 710 } 711 712 func (s *RangeService) checkRangeTypeIsFloat(startStr string, endStr string, stepStr string) ( 713 start float64, end float64, step float64, ok bool) { 714 715 stepStr = strings.ToLower(strings.TrimSpace(stepStr)) 716 717 start, errFloat1 := strconv.ParseFloat(startStr, 64) 718 end, errFloat2 := strconv.ParseFloat(endStr, 64) 719 var errFloat3 error 720 721 if stepStr != "" && stepStr != "r" { 722 step, errFloat3 = strconv.ParseFloat(stepStr, 64) 723 } else { 724 step = 0 725 } 726 727 if errFloat1 == nil && errFloat2 == nil && errFloat3 == nil { // is float 728 if (start > end && step > 0) || (start < end && step < 0) { 729 step = -1 * step 730 } 731 732 ok = true 733 return 734 } 735 736 return 737 } 738 739 func (s *RangeService) GetRandFieldSection(pth string) (key int) { 740 max := 0 741 742 for k, v := range vari.GlobalVars.RandFieldSectionShortKeysToPathMap { 743 if pth == v { 744 key = k 745 return 746 } 747 748 if k > max { 749 max = k 750 } 751 } 752 753 if key == 0 { 754 key = max + 1 755 } 756 757 return 758 }