github.com/kumasuke120/mockuma@v1.1.9/internal/mckmaps/mappings.go (about) 1 package mckmaps 2 3 import ( 4 "errors" 5 "fmt" 6 "net/url" 7 "regexp" 8 "sort" 9 "strconv" 10 "strings" 11 12 "github.com/kumasuke120/mockuma/internal/myhttp" 13 "github.com/kumasuke120/mockuma/internal/myjson" 14 "github.com/kumasuke120/mockuma/internal/types" 15 ) 16 17 type Mapping struct { 18 URI string 19 Method myhttp.HTTPMethod 20 Policies []*Policy 21 } 22 23 type Policy struct { 24 When *When 25 CmdType CmdType 26 Returns *Returns 27 Forwards *Forwards 28 } 29 30 type When struct { 31 Headers []*NameValuesPair 32 HeaderRegexps []*NameRegexpPair 33 HeaderJSONs []*NameJSONPair 34 35 Params []*NameValuesPair 36 ParamRegexps []*NameRegexpPair 37 ParamJSONs []*NameJSONPair 38 39 PathVars []*NameValuesPair 40 PathVarRegexps []*NameRegexpPair 41 42 Body []byte 43 BodyRegexp *regexp.Regexp 44 BodyJSON *myjson.ExtJSONMatcher 45 } 46 47 type CmdType string 48 49 const ( 50 CmdTypeReturns = CmdType(mapPolicyReturns) 51 CmdTypeForwards = CmdType(mapPolicyForwards) 52 CmdTypeRedirects = CmdType(mapPolicyRedirects) 53 ) 54 55 type Returns struct { 56 StatusCode myhttp.StatusCode 57 Headers []*NameValuesPair 58 Body []byte 59 Latency *Interval 60 } 61 62 type Forwards struct { 63 Path string 64 Latency *Interval 65 } 66 67 type NameValuesPair struct { 68 Name string 69 Values []string 70 } 71 72 type NameRegexpPair struct { 73 Name string 74 Regexp *regexp.Regexp 75 } 76 77 type NameJSONPair struct { 78 Name string 79 JSON myjson.ExtJSONMatcher 80 } 81 82 type Interval struct { 83 Min int64 84 Max int64 85 } 86 87 var ( 88 // refers to: https://tools.ietf.org/html/rfc7230#section-3.2.6 89 methodRegexp = regexp.MustCompile("(?i)^[-!#$%&'*+._`|~\\da-z]+$") 90 pathRegexp = regexp.MustCompile("^(?:https?://)?.+$") 91 pathVarRegexp = regexp.MustCompile("{[^}]*}") 92 ) 93 94 type mappingsParser struct { 95 json interface{} 96 jsonPath *myjson.Path 97 Parser 98 } 99 100 func (p *mappingsParser) parse() ([]*Mapping, error) { 101 if p.json == nil { 102 // file has been recorded in mainParser 103 json, err := p.load(false, ppRemoveComment, ppRenderTemplate) 104 if err != nil { 105 return nil, err 106 } 107 p.json = json 108 } 109 110 var rawMappings myjson.Array 111 switch p.json.(type) { 112 case myjson.Object: // parses in multi-file mode 113 p.jsonPath = myjson.NewPath("") 114 jsonObject := p.json.(myjson.Object) 115 116 p.jsonPath.SetLast(aType) 117 _type, err := jsonObject.GetString(aType) 118 if err != nil || string(_type) != tMappings { 119 return nil, p.newJSONParseError(p.jsonPath) 120 } 121 122 p.jsonPath.SetLast(tMappings) 123 rawMappings = ensureJSONArray(jsonObject.Get(tMappings)) 124 case myjson.Array: // parses in single-file mode 125 p.jsonPath = myjson.NewPath() 126 rawMappings = p.json.(myjson.Array) 127 default: 128 p.jsonPath = myjson.NewPath() 129 return nil, p.newJSONParseError(p.jsonPath) 130 } 131 132 p.jsonPath.Append(0) 133 var mappings []*Mapping 134 for idx, rm := range rawMappings { // parses each mapping 135 p.jsonPath.SetLast(idx) 136 137 switch rm.(type) { 138 case myjson.Object: 139 mapping, err := p.parseMapping(rm.(myjson.Object)) 140 if err != nil { 141 return nil, err 142 } 143 mappings = append(mappings, mapping) 144 default: 145 return nil, p.newJSONParseError(p.jsonPath) 146 } 147 } 148 p.jsonPath.RemoveLast() 149 150 return mappings, nil 151 } 152 153 func (p *mappingsParser) parseMapping(v myjson.Object) (*Mapping, error) { 154 p.jsonPath.Append("") 155 156 mapping := new(Mapping) 157 158 p.jsonPath.SetLast(aMapURI) 159 uri, err := v.GetString(aMapURI) 160 if err != nil { 161 return nil, p.newJSONParseError(p.jsonPath) 162 } 163 _uri, err := encodeURI(string(uri)) // encodes non-ascii characters 164 if err != nil { 165 return nil, &parserError{filename: p.filename, jsonPath: p.jsonPath, err: err} 166 } 167 mapping.URI = _uri 168 169 p.jsonPath.SetLast(aMapMethod) 170 if v.Has(aMapMethod) { 171 method, err := v.GetString(aMapMethod) 172 if err != nil { 173 return nil, p.newJSONParseError(p.jsonPath) 174 } 175 _method := string(method) 176 if methodRegexp.MatchString(_method) { 177 mapping.Method = myhttp.ToHTTPMethod(_method) 178 } else { 179 return nil, p.newJSONParseError(p.jsonPath) 180 } 181 } else { 182 mapping.Method = myhttp.MethodAny 183 } 184 185 p.jsonPath.SetLast(aMapPolicies) 186 p.jsonPath.Append(0) 187 var policies []*Policy 188 for idx, rp := range ensureJSONArray(v.Get(aMapPolicies)) { // parses each policy 189 p.jsonPath.SetLast(idx) 190 191 switch rp.(type) { 192 case myjson.Object: 193 policy, err := p.parsePolicy(rp.(myjson.Object)) 194 if err != nil { 195 return nil, err 196 } 197 policies = append(policies, policy) 198 default: 199 return nil, p.newJSONParseError(p.jsonPath) 200 } 201 } 202 p.jsonPath.RemoveLast() 203 mapping.Policies = policies 204 205 p.renamePathVars(mapping) 206 207 p.jsonPath.RemoveLast() 208 return mapping, nil 209 } 210 211 func encodeURI(uri string) (string, error) { 212 if strings.HasPrefix(uri, "/") { 213 indices := pathVarRegexp.FindAllStringIndex(uri, -1) 214 if indices == nil { 215 return doEncodeURI(uri), nil 216 } else { 217 var builder strings.Builder 218 for i, loc := range indices { 219 var startPos int 220 if i == 0 { 221 startPos = 0 222 } else { 223 startPos = indices[i-1][1] 224 } 225 builder.WriteString(doEncodeURI(uri[startPos:loc[0]])) 226 227 builder.WriteString(uri[loc[0]:loc[1]]) // skips pathVars 228 229 if i == len(indices)-1 { 230 builder.WriteString(doEncodeURI(uri[loc[1]:])) 231 } 232 } 233 return builder.String(), nil 234 } 235 } else { 236 return "", errors.New("uri must start with '/'") 237 } 238 } 239 240 func doEncodeURI(uri string) string { 241 encoded := &url.URL{Path: uri} 242 return encoded.String() 243 } 244 245 func (p *mappingsParser) parsePolicy(v myjson.Object) (*Policy, error) { 246 p.jsonPath.Append("") 247 248 policy := new(Policy) 249 250 p.jsonPath.SetLast(mapPolicyWhen) 251 var when *When 252 if v.Has(mapPolicyWhen) { 253 rawWhen, err := v.GetObject(mapPolicyWhen) 254 if err != nil { 255 return nil, p.newJSONParseError(p.jsonPath) 256 } 257 when, err = p.parseWhen(rawWhen) 258 if err != nil { 259 return nil, err 260 } 261 policy.When = when 262 } 263 264 cntCommands := p.countCommands(v, mapPolicyCommands...) 265 if cntCommands == 0 { // sets the default command when no command found in the policy 266 policy.Returns = &Returns{ 267 StatusCode: myhttp.StatusOK, 268 } 269 policy.CmdType = CmdTypeReturns 270 } else if cntCommands == 1 { 271 err := p.parseCommand(policy, v) 272 if err != nil { 273 return nil, err 274 } 275 } else { // multiple commands are not allowed in the same policy 276 return nil, &parserError{ 277 filename: p.filename, 278 jsonPath: p.jsonPath, 279 err: errors.New(fmt.Sprintf("commands%v can only be used one at a time", 280 mapPolicyCommands)), 281 } 282 } 283 284 p.jsonPath.RemoveLast() 285 return policy, nil 286 } 287 288 func (p *mappingsParser) parseCommand(dst *Policy, v myjson.Object) error { 289 if v.Has(mapPolicyReturns) { 290 p.jsonPath.SetLast(mapPolicyReturns) 291 rawReturns, err := v.GetObject(mapPolicyReturns) 292 if err != nil { 293 return p.newJSONParseError(p.jsonPath) 294 } 295 returns, err := p.parseReturns(rawReturns) 296 if err != nil { 297 return err 298 } 299 dst.Returns = returns 300 dst.CmdType = CmdTypeReturns 301 } else if v.Has(mapPolicyRedirects) { 302 p.jsonPath.SetLast(mapPolicyRedirects) 303 rawRedirects, err := v.GetObject(mapPolicyRedirects) 304 if err != nil { 305 return p.newJSONParseError(p.jsonPath) 306 } 307 redirects, err := p.parseRedirects(rawRedirects) 308 if err != nil { 309 return err 310 } 311 dst.Returns = redirects 312 dst.CmdType = CmdTypeRedirects 313 } else { 314 p.jsonPath.SetLast(mapPolicyForwards) 315 rawForwards, err := v.GetObject(mapPolicyForwards) 316 if err != nil { 317 return p.newJSONParseError(p.jsonPath) 318 } 319 forwards, err := p.parseForwards(rawForwards) 320 if err != nil { 321 return err 322 } 323 dst.Forwards = forwards 324 dst.CmdType = CmdTypeForwards 325 } 326 327 return nil 328 } 329 330 func (p *mappingsParser) countCommands(v myjson.Object, names ...string) int { 331 cnt := 0 332 for _, name := range names { 333 if v.Has(name) { 334 cnt++ 335 } 336 } 337 return cnt 338 } 339 340 func (p *mappingsParser) parseWhen(v myjson.Object) (*When, error) { 341 p.jsonPath.Append("") 342 343 _v, err := types.DoFiltersOnV(v, ppToJSONMatcher, ppParseRegexp, ppLoadFile) 344 if err != nil { 345 return nil, &loadError{filename: p.filename, err: err} 346 } 347 switch _v.(type) { 348 case myjson.Object: 349 v = _v.(myjson.Object) 350 default: 351 return nil, p.newJSONParseError(p.jsonPath) 352 } 353 354 when := new(When) 355 356 p.jsonPath.SetLast(pHeaders) 357 if v.Has(pHeaders) { 358 rawHeaders, err := v.GetObject(pHeaders) 359 if err != nil { 360 return nil, p.newJSONParseError(p.jsonPath) 361 } 362 363 normalHeaders, regexpHeaders, jsonMHeaders := divideIntoWhenMatchers(rawHeaders) 364 when.Headers = parseAsNameValuesPairs(normalHeaders) 365 when.HeaderRegexps = parseAsNameRegexpPairs(regexpHeaders) 366 when.HeaderJSONs = parseAsNameJSONPairs(jsonMHeaders) 367 } 368 369 p.jsonPath.SetLast(pParams) 370 if v.Has(pParams) { 371 rawParams, err := v.GetObject(pParams) 372 if err != nil { 373 return nil, p.newJSONParseError(p.jsonPath) 374 } 375 376 normalParams, regexpParams, jsonMHeaders := divideIntoWhenMatchers(rawParams) 377 when.Params = parseAsNameValuesPairs(normalParams) 378 when.ParamRegexps = parseAsNameRegexpPairs(regexpParams) 379 when.ParamJSONs = parseAsNameJSONPairs(jsonMHeaders) 380 } 381 382 p.jsonPath.SetLast(pPathVars) 383 if v.Has(pPathVars) { 384 rawPathVars, err := v.GetObject(pPathVars) 385 if err != nil { 386 return nil, p.newJSONParseError(p.jsonPath) 387 } 388 389 normalPathVars, regexpPathVars, jsonMPathVars := divideIntoWhenMatchers(rawPathVars) 390 when.PathVars = parseAsNameValuesPairs(normalPathVars) 391 when.PathVarRegexps = parseAsNameRegexpPairs(regexpPathVars) 392 if len(jsonMPathVars) != 0 { 393 return nil, p.newJSONParseError(p.jsonPath) 394 } 395 } 396 397 p.jsonPath.SetLast(pBody) 398 if v.Has(pBody) { 399 rawBody := v.Get(pBody) 400 bytes, bodyRegexp, jMatcher := p.parseWhenBody(rawBody) 401 when.Body = bytes 402 when.BodyRegexp = bodyRegexp 403 when.BodyJSON = jMatcher 404 } 405 406 p.jsonPath.RemoveLast() 407 return when, nil 408 } 409 410 func (p *mappingsParser) parseWhenBody(v interface{}) ([]byte, myjson.ExtRegexp, *myjson.ExtJSONMatcher) { 411 switch v.(type) { 412 case myjson.String: 413 return []byte(v.(myjson.String)), nil, nil 414 case myjson.ExtRegexp: 415 return nil, v.(myjson.ExtRegexp), nil 416 case myjson.ExtJSONMatcher: 417 _v := v.(myjson.ExtJSONMatcher) 418 return nil, nil, &_v 419 default: 420 return []byte(types.ToString(v)), nil, nil 421 } 422 } 423 424 func (p *mappingsParser) parseReturns(v myjson.Object) (*Returns, error) { 425 p.jsonPath.Append("") 426 427 returns := new(Returns) 428 429 p.jsonPath.SetLast(pStatusCode) 430 if v.Has(pStatusCode) { 431 statusCode, err := v.GetNumber(pStatusCode) 432 if err != nil { 433 return nil, p.newJSONParseError(p.jsonPath) 434 } 435 returns.StatusCode = myhttp.StatusCode(int(statusCode)) 436 } else { 437 returns.StatusCode = myhttp.StatusOK 438 } 439 440 p.jsonPath.SetLast(pHeaders) 441 if v.Has(pHeaders) { 442 rawHeaders, err := v.GetObject(pHeaders) 443 if err != nil { 444 return nil, p.newJSONParseError(p.jsonPath) 445 } 446 returns.Headers = parseAsNameValuesPairs(rawHeaders) 447 } 448 449 p.jsonPath.SetLast(pBody) 450 rawBody := v.Get(pBody) 451 body, err := p.parseReturnsBody(rawBody) 452 if err != nil { 453 return nil, p.newJSONParseError(p.jsonPath) 454 } 455 returns.Body = body 456 457 p.jsonPath.SetLast(pLatency) 458 if v.Has(pLatency) { 459 rawLatency := v.Get(pLatency) 460 latency, err := p.parseLatency(rawLatency) 461 if err != nil { 462 return nil, p.newJSONParseError(p.jsonPath) 463 } 464 returns.Latency = latency 465 } 466 467 p.jsonPath.RemoveLast() 468 return returns, nil 469 } 470 471 func (p *mappingsParser) parseJSONToBytes(v interface{}) ([]byte, error) { 472 bytes, err := myjson.Marshal(v) 473 if err != nil { 474 return nil, p.newJSONParseError(p.jsonPath) 475 } 476 return bytes, nil 477 } 478 479 func (p *mappingsParser) parseReturnsBody(v interface{}) ([]byte, error) { 480 v, err := types.DoFiltersOnV(v, ppLoadFile) 481 if err != nil { 482 return nil, &loadError{filename: p.filename, err: err} 483 } 484 485 switch v.(type) { 486 case nil: 487 return nil, nil 488 case myjson.String: 489 return []byte(string(v.(myjson.String))), nil 490 case myjson.Object: 491 return p.parseJSONToBytes(v) 492 case myjson.Array: 493 return p.parseJSONToBytes(v) 494 } 495 496 return nil, p.newJSONParseError(p.jsonPath) 497 } 498 499 func (p *mappingsParser) parseForwards(v myjson.Object) (*Forwards, error) { 500 p.jsonPath.Append("") 501 502 forwards := new(Forwards) 503 504 p.jsonPath.SetLast(pPath) 505 path, err := v.GetString(pPath) 506 if err != nil || !pathRegexp.MatchString(string(path)) { // checks path 507 return nil, p.newJSONParseError(p.jsonPath) 508 } 509 forwards.Path = string(path) 510 511 p.jsonPath.SetLast(pLatency) 512 if v.Has(pLatency) { 513 rawLatency := v.Get(pLatency) 514 latency, err := p.parseLatency(rawLatency) 515 if err != nil { 516 return nil, p.newJSONParseError(p.jsonPath) 517 } 518 forwards.Latency = latency 519 } 520 521 p.jsonPath.RemoveLast() 522 return forwards, nil 523 } 524 525 func (p *mappingsParser) parseRedirects(v myjson.Object) (*Returns, error) { 526 p.jsonPath.Append("") 527 528 returns := &Returns{StatusCode: myhttp.StatusFound} 529 530 p.jsonPath.SetLast(pPath) 531 path, err := v.GetString(pPath) 532 if err != nil || !pathRegexp.MatchString(string(path)) { // checks path 533 return nil, p.newJSONParseError(p.jsonPath) 534 } 535 returns.Headers = []*NameValuesPair{ 536 { 537 Name: myhttp.HeaderLocation, 538 Values: []string{string(path)}, 539 }, 540 } 541 542 p.jsonPath.SetLast(pLatency) 543 if v.Has(pLatency) { 544 rawLatency := v.Get(pLatency) 545 latency, err := p.parseLatency(rawLatency) 546 if err != nil { 547 return nil, p.newJSONParseError(p.jsonPath) 548 } 549 returns.Latency = latency 550 } 551 552 p.jsonPath.RemoveLast() 553 return returns, nil 554 } 555 556 func (p *mappingsParser) parseLatency(v interface{}) (*Interval, error) { 557 switch v.(type) { 558 case myjson.Number: 559 _v := int64(v.(myjson.Number)) 560 return &Interval{ 561 Min: _v, 562 Max: _v, 563 }, nil 564 case myjson.Array: 565 va := v.(myjson.Array) 566 if len(va) == 1 { // treats 1-element array as number 567 va0 := va[0] 568 switch va0.(type) { 569 case myjson.Number: 570 return p.parseLatency(va0) 571 } 572 } else if len(va) == 2 { 573 if myjson.IsAllNumber(va) { 574 va0 := int64(va[0].(myjson.Number)) // 0 as min 575 va1 := int64(va[1].(myjson.Number)) // 1 as max 576 if va1 >= va0 { 577 return &Interval{ 578 Min: va0, 579 Max: va1, 580 }, nil 581 } 582 } 583 } 584 } 585 586 return nil, p.newJSONParseError(p.jsonPath) 587 } 588 589 // numbers all pathVars, giving each pathVar an independent index. 590 // changes pathVars' names to the corresponding indices. 591 // pathVars with same names share the same index. 592 func (p *mappingsParser) renamePathVars(mapping *Mapping) { 593 newURI, var2Idx := numberPathVars(mapping.URI) 594 mapping.URI = newURI 595 596 for _, pol := range mapping.Policies { 597 when := pol.When 598 if when != nil { 599 l := len(when.PathVars) 600 if l != 0 { 601 newPVars := p.numberForPathVars(l, when, var2Idx) 602 p.sortPathVars(newPVars) 603 when.PathVars = newPVars 604 } 605 606 l = len(when.PathVarRegexps) 607 if l != 0 { 608 newPVarRegexps := p.numberForPathVarRegexps(when, var2Idx) 609 p.sortPathVarRegexps(newPVarRegexps) 610 when.PathVarRegexps = newPVarRegexps 611 } 612 } 613 } 614 } 615 616 func (p *mappingsParser) numberForPathVars(l int, when *When, var2Idx map[string]int) []*NameValuesPair { 617 newPVars := make([]*NameValuesPair, l) 618 for i, v := range when.PathVars { 619 if idx, ok := var2Idx[v.Name]; ok { 620 newPVars[i] = &NameValuesPair{ 621 Name: strconv.Itoa(idx), 622 Values: v.Values, 623 } 624 } else { 625 panic("Shouldn't happen") 626 } 627 } 628 return newPVars 629 } 630 631 func (p *mappingsParser) numberForPathVarRegexps(when *When, var2Idx map[string]int) []*NameRegexpPair { 632 newPVarRegexps := make([]*NameRegexpPair, len(when.PathVarRegexps)) 633 for i, v := range when.PathVarRegexps { 634 if idx, ok := var2Idx[v.Name]; ok { 635 newPVarRegexps[i] = &NameRegexpPair{ 636 Name: strconv.Itoa(idx), 637 Regexp: v.Regexp, 638 } 639 } else { 640 panic("Shouldn't happen") 641 } 642 } 643 return newPVarRegexps 644 } 645 646 func (p *mappingsParser) sortPathVars(newPVars []*NameValuesPair) { 647 sort.Slice(newPVars, func(i, j int) bool { 648 var err error 649 var a, b int 650 a, err = strconv.Atoi(newPVars[i].Name) 651 if err != nil { 652 panic("Shouldn't happen") 653 } 654 b, err = strconv.Atoi(newPVars[j].Name) 655 if err != nil { 656 panic("Shouldn't happen") 657 } 658 return a < b 659 }) 660 } 661 662 func (p *mappingsParser) sortPathVarRegexps(newPVars []*NameRegexpPair) { 663 sort.Slice(newPVars, func(i, j int) bool { 664 var err error 665 var a, b int 666 a, err = strconv.Atoi(newPVars[i].Name) 667 if err != nil { 668 panic("Shouldn't happen") 669 } 670 b, err = strconv.Atoi(newPVars[j].Name) 671 if err != nil { 672 panic("Shouldn't happen") 673 } 674 return a < b 675 }) 676 } 677 678 func numberPathVars(uri string) (n string, m map[string]int) { 679 m = make(map[string]int) 680 n = pathVarRegexp.ReplaceAllStringFunc(uri, (func() func(string) string { 681 idx := 0 682 return func(s string) string { 683 var i int 684 var ok bool 685 name := s[1 : len(s)-1] 686 if i, ok = m[name]; !ok { 687 i = idx 688 idx++ 689 m[name] = i 690 } 691 return fmt.Sprintf("{%d}", i) 692 } 693 })()) 694 return 695 } 696 697 // divides matchers into normal, regexp, json 698 func divideIntoWhenMatchers(v myjson.Object) (myjson.Object, 699 map[string]myjson.ExtRegexp, map[string]myjson.ExtJSONMatcher) { 700 701 direct := make(myjson.Object) 702 regexps := make(map[string]myjson.ExtRegexp) 703 jsonMatchers := make(map[string]myjson.ExtJSONMatcher) 704 705 for name, rawValue := range v { 706 var normV myjson.Array 707 708 for _, rV := range ensureJSONArray(rawValue) { // divides normals and regexps 709 switch rV.(type) { 710 case myjson.ExtRegexp: 711 _rV := rV.(myjson.ExtRegexp) 712 if _, ok := regexps[name]; !ok { // only first @regexp is effective 713 regexps[name] = _rV 714 } 715 continue 716 case myjson.ExtJSONMatcher: 717 _rV := rV.(myjson.ExtJSONMatcher) 718 if _, ok := jsonMatchers[name]; !ok { // only first @json is effective 719 jsonMatchers[name] = _rV 720 } 721 continue 722 } 723 normV = append(normV, rV) 724 } 725 726 if len(normV) != 0 { 727 direct[name] = normV 728 } 729 } 730 731 return direct, regexps, jsonMatchers 732 } 733 734 func parseAsNameValuesPairs(o myjson.Object) []*NameValuesPair { 735 var pairs []*NameValuesPair 736 for name, rawValues := range o { 737 p := parseAsNameValuesPair(name, ensureJSONArray(rawValues)) 738 pairs = append(pairs, p) 739 } 740 return pairs 741 } 742 743 func parseAsNameValuesPair(n string, v myjson.Array) *NameValuesPair { 744 pair := new(NameValuesPair) 745 746 pair.Name = n 747 748 values := make([]string, len(v)) 749 for i, p := range v { 750 switch p.(type) { 751 case nil: 752 values[i] = "" 753 default: 754 str, err := myjson.ToString(p) 755 if err != nil { 756 panic("Shouldn't happen") 757 } 758 values[i] = string(str) 759 } 760 761 } 762 pair.Values = values 763 764 return pair 765 } 766 767 func parseAsNameRegexpPairs(o map[string]myjson.ExtRegexp) []*NameRegexpPair { 768 var pairs []*NameRegexpPair 769 for name, value := range o { 770 pair := new(NameRegexpPair) 771 pair.Name = name 772 pair.Regexp = value 773 774 pairs = append(pairs, pair) 775 } 776 return pairs 777 } 778 779 func parseAsNameJSONPairs(o map[string]myjson.ExtJSONMatcher) []*NameJSONPair { 780 var pairs []*NameJSONPair 781 for name, value := range o { 782 pair := new(NameJSONPair) 783 pair.Name = name 784 pair.JSON = value 785 786 pairs = append(pairs, pair) 787 } 788 return pairs 789 }