You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
81 lines
1.9 KiB
81 lines
1.9 KiB
package query
|
|
|
|
import (
|
|
"io"
|
|
"net/url"
|
|
"sort"
|
|
)
|
|
|
|
// Encoder is a Query encoder that supports construction of Query body
|
|
// values using methods.
|
|
type Encoder struct {
|
|
// The query values that will be built up to manage encoding.
|
|
values url.Values
|
|
// The writer that the encoded body will be written to.
|
|
writer io.Writer
|
|
Value
|
|
}
|
|
|
|
// NewEncoder returns a new Query body encoder
|
|
func NewEncoder(writer io.Writer) *Encoder {
|
|
values := url.Values{}
|
|
return &Encoder{
|
|
values: values,
|
|
writer: writer,
|
|
Value: newBaseValue(values),
|
|
}
|
|
}
|
|
|
|
// Encode returns the []byte slice representing the current
|
|
// state of the Query encoder.
|
|
func (e Encoder) Encode() error {
|
|
ws, ok := e.writer.(interface{ WriteString(string) (int, error) })
|
|
if !ok {
|
|
// Fall back to less optimal byte slice casting if WriteString isn't available.
|
|
ws = &wrapWriteString{writer: e.writer}
|
|
}
|
|
|
|
// Get the keys and sort them to have a stable output
|
|
keys := make([]string, 0, len(e.values))
|
|
for k := range e.values {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
isFirstEntry := true
|
|
for _, key := range keys {
|
|
queryValues := e.values[key]
|
|
escapedKey := url.QueryEscape(key)
|
|
for _, value := range queryValues {
|
|
if !isFirstEntry {
|
|
if _, err := ws.WriteString(`&`); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
isFirstEntry = false
|
|
}
|
|
if _, err := ws.WriteString(escapedKey); err != nil {
|
|
return err
|
|
}
|
|
if _, err := ws.WriteString(`=`); err != nil {
|
|
return err
|
|
}
|
|
if _, err := ws.WriteString(url.QueryEscape(value)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// wrapWriteString wraps an io.Writer to provide a WriteString method
|
|
// where one is not available.
|
|
type wrapWriteString struct {
|
|
writer io.Writer
|
|
}
|
|
|
|
// WriteString writes a string to the wrapped writer by casting it to
|
|
// a byte array first.
|
|
func (w wrapWriteString) WriteString(v string) (int, error) {
|
|
return w.writer.Write([]byte(v))
|
|
}
|