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  }