1 package autorest
2 3 // Copyright 2017 Microsoft Corporation
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 17 import (
18 "bytes"
19 "encoding/json"
20 "encoding/xml"
21 "fmt"
22 "io"
23 "net"
24 "net/http"
25 "net/url"
26 "reflect"
27 "strings"
28 )
29 30 // EncodedAs is a series of constants specifying various data encodings
31 type EncodedAs string
32 33 const (
34 // EncodedAsJSON states that data is encoded as JSON
35 EncodedAsJSON EncodedAs = "JSON"
36 37 // EncodedAsXML states that data is encoded as Xml
38 EncodedAsXML EncodedAs = "XML"
39 )
40 41 // Decoder defines the decoding method json.Decoder and xml.Decoder share
42 type Decoder interface {
43 Decode(v interface{}) error
44 }
45 46 // NewDecoder creates a new decoder appropriate to the passed encoding.
47 // encodedAs specifies the type of encoding and r supplies the io.Reader containing the
48 // encoded data.
49 func NewDecoder(encodedAs EncodedAs, r io.Reader) Decoder {
50 if encodedAs == EncodedAsJSON {
51 return json.NewDecoder(r)
52 } else if encodedAs == EncodedAsXML {
53 return xml.NewDecoder(r)
54 }
55 return nil
56 }
57 58 // CopyAndDecode decodes the data from the passed io.Reader while making a copy. Having a copy
59 // is especially useful if there is a chance the data will fail to decode.
60 // encodedAs specifies the expected encoding, r provides the io.Reader to the data, and v
61 // is the decoding destination.
62 func CopyAndDecode(encodedAs EncodedAs, r io.Reader, v interface{}) (b bytes.Buffer, err error) {
63 err = NewDecoder(encodedAs, io.TeeReader(r, &b)).Decode(v)
64 return
65 }
66 67 // TeeReadCloser returns a ReadCloser that writes to w what it reads from rc.
68 // It utilizes io.TeeReader to copy the data read and has the same behavior when reading.
69 // Further, when it is closed, it ensures that rc is closed as well.
70 func TeeReadCloser(rc io.ReadCloser, w io.Writer) io.ReadCloser {
71 return &teeReadCloser{rc, io.TeeReader(rc, w)}
72 }
73 74 type teeReadCloser struct {
75 rc io.ReadCloser
76 r io.Reader
77 }
78 79 func (t *teeReadCloser) Read(p []byte) (int, error) {
80 return t.r.Read(p)
81 }
82 83 func (t *teeReadCloser) Close() error {
84 return t.rc.Close()
85 }
86 87 func containsInt(ints []int, n int) bool {
88 for _, i := range ints {
89 if i == n {
90 return true
91 }
92 }
93 return false
94 }
95 96 func escapeValueStrings(m map[string]string) map[string]string {
97 for key, value := range m {
98 m[key] = url.QueryEscape(value)
99 }
100 return m
101 }
102 103 func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string {
104 mapOfStrings := make(map[string]string)
105 for key, value := range mapOfInterface {
106 mapOfStrings[key] = ensureValueString(value)
107 }
108 return mapOfStrings
109 }
110 111 func ensureValueString(value interface{}) string {
112 if value == nil {
113 return ""
114 }
115 switch v := value.(type) {
116 case string:
117 return v
118 case []byte:
119 return string(v)
120 default:
121 return fmt.Sprintf("%v", v)
122 }
123 }
124 125 // MapToValues method converts map[string]interface{} to url.Values.
126 func MapToValues(m map[string]interface{}) url.Values {
127 v := url.Values{}
128 for key, value := range m {
129 x := reflect.ValueOf(value)
130 if x.Kind() == reflect.Array || x.Kind() == reflect.Slice {
131 for i := 0; i < x.Len(); i++ {
132 v.Add(key, ensureValueString(x.Index(i)))
133 }
134 } else {
135 v.Add(key, ensureValueString(value))
136 }
137 }
138 return v
139 }
140 141 // AsStringSlice method converts interface{} to []string.
142 // s must be of type slice or array or an error is returned.
143 // Each element of s will be converted to its string representation.
144 func AsStringSlice(s interface{}) ([]string, error) {
145 v := reflect.ValueOf(s)
146 if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
147 return nil, NewError("autorest", "AsStringSlice", "the value's type is not a slice or array.")
148 }
149 stringSlice := make([]string, 0, v.Len())
150 151 for i := 0; i < v.Len(); i++ {
152 stringSlice = append(stringSlice, fmt.Sprintf("%v", v.Index(i)))
153 }
154 return stringSlice, nil
155 }
156 157 // String method converts interface v to string. If interface is a list, it
158 // joins list elements using the separator. Note that only sep[0] will be used for
159 // joining if any separator is specified.
160 func String(v interface{}, sep ...string) string {
161 if len(sep) == 0 {
162 return ensureValueString(v)
163 }
164 stringSlice, ok := v.([]string)
165 if ok == false {
166 var err error
167 stringSlice, err = AsStringSlice(v)
168 if err != nil {
169 panic(fmt.Sprintf("autorest: Couldn't convert value to a string %s.", err))
170 }
171 }
172 return ensureValueString(strings.Join(stringSlice, sep[0]))
173 }
174 175 // Encode method encodes url path and query parameters.
176 func Encode(location string, v interface{}, sep ...string) string {
177 s := String(v, sep...)
178 switch strings.ToLower(location) {
179 case "path":
180 return pathEscape(s)
181 case "query":
182 return queryEscape(s)
183 default:
184 return s
185 }
186 }
187 188 func pathEscape(s string) string {
189 return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
190 }
191 192 func queryEscape(s string) string {
193 return url.QueryEscape(s)
194 }
195 196 // ChangeToGet turns the specified http.Request into a GET (it assumes it wasn't).
197 // This is mainly useful for long-running operations that use the Azure-AsyncOperation
198 // header, so we change the initial PUT into a GET to retrieve the final result.
199 func ChangeToGet(req *http.Request) *http.Request {
200 req.Method = "GET"
201 req.Body = nil
202 req.ContentLength = 0
203 req.Header.Del("Content-Length")
204 return req
205 }
206 207 // IsTemporaryNetworkError returns true if the specified error is a temporary network error or false
208 // if it's not. If the error doesn't implement the net.Error interface the return value is true.
209 func IsTemporaryNetworkError(err error) bool {
210 if netErr, ok := err.(net.Error); !ok || (ok && netErr.Temporary()) {
211 return true
212 }
213 return false
214 }
215 216 // DrainResponseBody reads the response body then closes it.
217 func DrainResponseBody(resp *http.Response) error {
218 if resp != nil && resp.Body != nil {
219 _, err := io.Copy(io.Discard, resp.Body)
220 resp.Body.Close()
221 return err
222 }
223 return nil
224 }
225 226 func setHeader(r *http.Request, key, value string) {
227 if r.Header == nil {
228 r.Header = make(http.Header)
229 }
230 r.Header.Set(key, value)
231 }
232