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 }