encoder.go (1942B)
1 package query 2 3 import ( 4 "io" 5 "net/url" 6 "sort" 7 ) 8 9 // Encoder is a Query encoder that supports construction of Query body 10 // values using methods. 11 type Encoder struct { 12 // The query values that will be built up to manage encoding. 13 values url.Values 14 // The writer that the encoded body will be written to. 15 writer io.Writer 16 Value 17 } 18 19 // NewEncoder returns a new Query body encoder 20 func NewEncoder(writer io.Writer) *Encoder { 21 values := url.Values{} 22 return &Encoder{ 23 values: values, 24 writer: writer, 25 Value: newBaseValue(values), 26 } 27 } 28 29 // Encode returns the []byte slice representing the current 30 // state of the Query encoder. 31 func (e Encoder) Encode() error { 32 ws, ok := e.writer.(interface{ WriteString(string) (int, error) }) 33 if !ok { 34 // Fall back to less optimal byte slice casting if WriteString isn't available. 35 ws = &wrapWriteString{writer: e.writer} 36 } 37 38 // Get the keys and sort them to have a stable output 39 keys := make([]string, 0, len(e.values)) 40 for k := range e.values { 41 keys = append(keys, k) 42 } 43 sort.Strings(keys) 44 isFirstEntry := true 45 for _, key := range keys { 46 queryValues := e.values[key] 47 escapedKey := url.QueryEscape(key) 48 for _, value := range queryValues { 49 if !isFirstEntry { 50 if _, err := ws.WriteString(`&`); err != nil { 51 return err 52 } 53 } else { 54 isFirstEntry = false 55 } 56 if _, err := ws.WriteString(escapedKey); err != nil { 57 return err 58 } 59 if _, err := ws.WriteString(`=`); err != nil { 60 return err 61 } 62 if _, err := ws.WriteString(url.QueryEscape(value)); err != nil { 63 return err 64 } 65 } 66 } 67 return nil 68 } 69 70 // wrapWriteString wraps an io.Writer to provide a WriteString method 71 // where one is not available. 72 type wrapWriteString struct { 73 writer io.Writer 74 } 75 76 // WriteString writes a string to the wrapped writer by casting it to 77 // a byte array first. 78 func (w wrapWriteString) WriteString(v string) (int, error) { 79 return w.writer.Write([]byte(v)) 80 }