github.com/twilio/twilio-go@v1.20.1/client/form/node.go (about)

     1  // Forked code from https://github.com/ajg/form
     2  
     3  // Copyright 2014 Alvaro J. Genial. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  // nolint
     7  package form
     8  
     9  import (
    10  	"net/url"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  type node map[string]interface{}
    16  
    17  func (n node) values(d, e rune) url.Values {
    18  	vs := url.Values{}
    19  	n.merge(d, e, "", &vs)
    20  	return vs
    21  }
    22  
    23  func (n node) merge(d, e rune, p string, vs *url.Values) {
    24  	for k, x := range n {
    25  		switch y := x.(type) {
    26  		case string:
    27  			vs.Add(p+escape(d, e, k), y)
    28  		case node:
    29  			y.merge(d, e, p+escape(d, e, k)+string(d), vs)
    30  		default:
    31  			panic("value is neither string nor node")
    32  		}
    33  	}
    34  }
    35  
    36  // TODO: Add tests for implicit indexing.
    37  func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node {
    38  	// NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works:
    39  	//    i. At the first level;   e.g. Foo.Bar=A&Foo.Bar=B     becomes 0.Foo.Bar=A&1.Foo.Bar=B
    40  	//   ii. At the last level;    e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B
    41  	// TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B
    42  	//       (This last one requires that there only be one placeholder in order for it to be unambiguous.)
    43  
    44  	m := map[string]string{}
    45  	for k, ss := range vs {
    46  		indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey)
    47  
    48  		for i, s := range ss {
    49  			if canIndexFirstLevelOrdinally {
    50  				k = strconv.Itoa(i) + string(d) + k
    51  			} else if indexLastLevelOrdinally {
    52  				k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i)
    53  			}
    54  
    55  			m[k] = s
    56  		}
    57  	}
    58  
    59  	n := node{}
    60  	for k, s := range m {
    61  		n = n.split(d, e, k, s)
    62  	}
    63  	return n
    64  }
    65  
    66  func splitPath(d, e rune, path string) (k, rest string) {
    67  	esc := false
    68  	for i, r := range path {
    69  		switch {
    70  		case !esc && r == e:
    71  			esc = true
    72  		case !esc && r == d:
    73  			return unescape(d, e, path[:i]), path[i+1:]
    74  		default:
    75  			esc = false
    76  		}
    77  	}
    78  	return unescape(d, e, path), ""
    79  }
    80  
    81  func (n node) split(d, e rune, path, s string) node {
    82  	k, rest := splitPath(d, e, path)
    83  	if rest == "" {
    84  		return add(n, k, s)
    85  	}
    86  	if _, ok := n[k]; !ok {
    87  		n[k] = node{}
    88  	}
    89  
    90  	c := getNode(n[k])
    91  	n[k] = c.split(d, e, rest, s)
    92  	return n
    93  }
    94  
    95  func add(n node, k, s string) node {
    96  	if n == nil {
    97  		return node{k: s}
    98  	}
    99  
   100  	if _, ok := n[k]; ok {
   101  		panic("key " + k + " already set")
   102  	}
   103  
   104  	n[k] = s
   105  	return n
   106  }
   107  
   108  func isEmpty(x interface{}) bool {
   109  	switch y := x.(type) {
   110  	case string:
   111  		return y == ""
   112  	case node:
   113  		if s, ok := y[""].(string); ok {
   114  			return s == ""
   115  		}
   116  		return false
   117  	}
   118  	panic("value is neither string nor node")
   119  }
   120  
   121  func getNode(x interface{}) node {
   122  	switch y := x.(type) {
   123  	case string:
   124  		return node{"": y}
   125  	case node:
   126  		return y
   127  	}
   128  	panic("value is neither string nor node")
   129  }
   130  
   131  func getString(x interface{}) string {
   132  	switch y := x.(type) {
   133  	case string:
   134  		return y
   135  	case node:
   136  		if s, ok := y[""].(string); ok {
   137  			return s
   138  		}
   139  		return ""
   140  	}
   141  	panic("value is neither string nor node")
   142  }
   143  
   144  func escape(d, e rune, s string) string {
   145  	s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape    (\ => \\)
   146  	s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.)
   147  	return s
   148  }
   149  
   150  func unescape(d, e rune, s string) string {
   151  	s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .)
   152  	s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape    (\\ => \)
   153  	return s
   154  }