feat(train): add new command to interact with aws and train models

This commit is contained in:
2021-10-17 19:15:44 +02:00
parent 5436dfebc2
commit 538cea18f2
1064 changed files with 282251 additions and 89305 deletions

4
vendor/github.com/aws/smithy-go/encoding/doc.go generated vendored Normal file
View File

@ -0,0 +1,4 @@
// Package encoding provides utilities for encoding values for specific
// document encodings.
package encoding

40
vendor/github.com/aws/smithy-go/encoding/encoding.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
package encoding
import (
"fmt"
"math"
"strconv"
)
// EncodeFloat encodes a float value as per the stdlib encoder for json and xml protocol
// This encodes a float value into dst while attempting to conform to ES6 ToString for Numbers
//
// Based on encoding/json floatEncoder from the Go Standard Library
// https://golang.org/src/encoding/json/encode.go
func EncodeFloat(dst []byte, v float64, bits int) []byte {
if math.IsInf(v, 0) || math.IsNaN(v) {
panic(fmt.Sprintf("invalid float value: %s", strconv.FormatFloat(v, 'g', -1, bits)))
}
abs := math.Abs(v)
fmt := byte('f')
if abs != 0 {
if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
fmt = 'e'
}
}
dst = strconv.AppendFloat(dst, v, fmt, -1, bits)
if fmt == 'e' {
// clean up e-09 to e-9
n := len(dst)
if n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' {
dst[n-2] = dst[n-1]
dst = dst[:n-1]
}
}
return dst
}

View File

@ -0,0 +1,116 @@
package httpbinding
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
)
const (
contentLengthHeader = "Content-Length"
floatNaN = "NaN"
floatInfinity = "Infinity"
floatNegInfinity = "-Infinity"
)
// An Encoder provides encoding of REST URI path, query, and header components
// of an HTTP request. Can also encode a stream as the payload.
//
// Does not support SetFields.
type Encoder struct {
path, rawPath, pathBuffer []byte
query url.Values
header http.Header
}
// NewEncoder creates a new encoder from the passed in request. All query and
// header values will be added on top of the request's existing values. Overwriting
// duplicate values.
func NewEncoder(path, query string, headers http.Header) (*Encoder, error) {
parseQuery, err := url.ParseQuery(query)
if err != nil {
return nil, fmt.Errorf("failed to parse query string: %w", err)
}
e := &Encoder{
path: []byte(path),
rawPath: []byte(path),
query: parseQuery,
header: headers.Clone(),
}
return e, nil
}
// Encode returns a REST protocol encoder for encoding HTTP bindings.
//
// Due net/http requiring `Content-Length` to be specified on the http.Request#ContentLength directly. Encode
// will look for whether the header is present, and if so will remove it and set the respective value on http.Request.
//
// Returns any error if one occurred during encoding.
func (e *Encoder) Encode(req *http.Request) (*http.Request, error) {
req.URL.Path, req.URL.RawPath = string(e.path), string(e.rawPath)
req.URL.RawQuery = e.query.Encode()
// net/http ignores Content-Length header and requires it to be set on http.Request
if v := e.header.Get(contentLengthHeader); len(v) > 0 {
iv, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil, err
}
req.ContentLength = iv
e.header.Del(contentLengthHeader)
}
req.Header = e.header
return req, nil
}
// AddHeader returns a HeaderValue for appending to the given header name
func (e *Encoder) AddHeader(key string) HeaderValue {
return newHeaderValue(e.header, key, true)
}
// SetHeader returns a HeaderValue for setting the given header name
func (e *Encoder) SetHeader(key string) HeaderValue {
return newHeaderValue(e.header, key, false)
}
// Headers returns a Header used encoding headers with the given prefix
func (e *Encoder) Headers(prefix string) Headers {
return Headers{
header: e.header,
prefix: strings.TrimSpace(prefix),
}
}
// HasHeader returns if a header with the key specified exists with one or
// more value.
func (e Encoder) HasHeader(key string) bool {
return len(e.header[key]) != 0
}
// SetURI returns a URIValue used for setting the given path key
func (e *Encoder) SetURI(key string) URIValue {
return newURIValue(&e.path, &e.rawPath, &e.pathBuffer, key)
}
// SetQuery returns a QueryValue used for setting the given query key
func (e *Encoder) SetQuery(key string) QueryValue {
return NewQueryValue(e.query, key, false)
}
// AddQuery returns a QueryValue used for appending the given query key
func (e *Encoder) AddQuery(key string) QueryValue {
return NewQueryValue(e.query, key, true)
}
// HasQuery returns if a query with the key specified exists with one or
// more value.
func (e *Encoder) HasQuery(key string) bool {
return len(e.query.Get(key)) != 0
}

View File

@ -0,0 +1,122 @@
package httpbinding
import (
"encoding/base64"
"math"
"math/big"
"net/http"
"strconv"
"strings"
)
// Headers is used to encode header keys using a provided prefix
type Headers struct {
header http.Header
prefix string
}
// AddHeader returns a HeaderValue used to append values to prefix+key
func (h Headers) AddHeader(key string) HeaderValue {
return h.newHeaderValue(key, true)
}
// SetHeader returns a HeaderValue used to set the value of prefix+key
func (h Headers) SetHeader(key string) HeaderValue {
return h.newHeaderValue(key, false)
}
func (h Headers) newHeaderValue(key string, append bool) HeaderValue {
return newHeaderValue(h.header, h.prefix+strings.TrimSpace(key), append)
}
// HeaderValue is used to encode values to an HTTP header
type HeaderValue struct {
header http.Header
key string
append bool
}
func newHeaderValue(header http.Header, key string, append bool) HeaderValue {
return HeaderValue{header: header, key: strings.TrimSpace(key), append: append}
}
func (h HeaderValue) modifyHeader(value string) {
if h.append {
h.header[h.key] = append(h.header[h.key], value)
} else {
h.header[h.key] = append(h.header[h.key][:0], value)
}
}
// String encodes the value v as the header string value
func (h HeaderValue) String(v string) {
h.modifyHeader(v)
}
// Byte encodes the value v as a query string value
func (h HeaderValue) Byte(v int8) {
h.Long(int64(v))
}
// Short encodes the value v as a query string value
func (h HeaderValue) Short(v int16) {
h.Long(int64(v))
}
// Integer encodes the value v as the header string value
func (h HeaderValue) Integer(v int32) {
h.Long(int64(v))
}
// Long encodes the value v as the header string value
func (h HeaderValue) Long(v int64) {
h.modifyHeader(strconv.FormatInt(v, 10))
}
// Boolean encodes the value v as a query string value
func (h HeaderValue) Boolean(v bool) {
h.modifyHeader(strconv.FormatBool(v))
}
// Float encodes the value v as a query string value
func (h HeaderValue) Float(v float32) {
h.float(float64(v), 32)
}
// Double encodes the value v as a query string value
func (h HeaderValue) Double(v float64) {
h.float(v, 64)
}
func (h HeaderValue) float(v float64, bitSize int) {
switch {
case math.IsNaN(v):
h.String(floatNaN)
case math.IsInf(v, 1):
h.String(floatInfinity)
case math.IsInf(v, -1):
h.String(floatNegInfinity)
default:
h.modifyHeader(strconv.FormatFloat(v, 'f', -1, bitSize))
}
}
// BigInteger encodes the value v as a query string value
func (h HeaderValue) BigInteger(v *big.Int) {
h.modifyHeader(v.String())
}
// BigDecimal encodes the value v as a query string value
func (h HeaderValue) BigDecimal(v *big.Float) {
if i, accuracy := v.Int64(); accuracy == big.Exact {
h.Long(i)
return
}
h.modifyHeader(v.Text('e', -1))
}
// Blob encodes the value v as a base64 header string value
func (h HeaderValue) Blob(v []byte) {
encodeToString := base64.StdEncoding.EncodeToString(v)
h.modifyHeader(encodeToString)
}

View File

@ -0,0 +1,108 @@
package httpbinding
import (
"bytes"
"fmt"
)
const (
uriTokenStart = '{'
uriTokenStop = '}'
uriTokenSkip = '+'
)
func bufCap(b []byte, n int) []byte {
if cap(b) < n {
return make([]byte, 0, n)
}
return b[0:0]
}
// replacePathElement replaces a single element in the path []byte.
// Escape is used to control whether the value will be escaped using Amazon path escape style.
func replacePathElement(path, fieldBuf []byte, key, val string, escape bool) ([]byte, []byte, error) {
fieldBuf = bufCap(fieldBuf, len(key)+3) // { <key> [+] }
fieldBuf = append(fieldBuf, uriTokenStart)
fieldBuf = append(fieldBuf, key...)
start := bytes.Index(path, fieldBuf)
end := start + len(fieldBuf)
if start < 0 || len(path[end:]) == 0 {
// TODO what to do about error?
return path, fieldBuf, fmt.Errorf("invalid path index, start=%d,end=%d. %s", start, end, path)
}
encodeSep := true
if path[end] == uriTokenSkip {
// '+' token means do not escape slashes
encodeSep = false
end++
}
if escape {
val = EscapePath(val, encodeSep)
}
if path[end] != uriTokenStop {
return path, fieldBuf, fmt.Errorf("invalid path element, does not contain token stop, %s", path)
}
end++
fieldBuf = bufCap(fieldBuf, len(val))
fieldBuf = append(fieldBuf, val...)
keyLen := end - start
valLen := len(fieldBuf)
if keyLen == valLen {
copy(path[start:], fieldBuf)
return path, fieldBuf, nil
}
newLen := len(path) + (valLen - keyLen)
if len(path) < newLen {
path = path[:cap(path)]
}
if cap(path) < newLen {
newURI := make([]byte, newLen)
copy(newURI, path)
path = newURI
}
// shift
copy(path[start+valLen:], path[end:])
path = path[:newLen]
copy(path[start:], fieldBuf)
return path, fieldBuf, nil
}
// EscapePath escapes part of a URL path in Amazon style.
func EscapePath(path string, encodeSep bool) string {
var buf bytes.Buffer
for i := 0; i < len(path); i++ {
c := path[i]
if noEscape[c] || (c == '/' && !encodeSep) {
buf.WriteByte(c)
} else {
fmt.Fprintf(&buf, "%%%02X", c)
}
}
return buf.String()
}
var noEscape [256]bool
func init() {
for i := 0; i < len(noEscape); i++ {
// AWS expects every character except these to be escaped
noEscape[i] = (i >= 'A' && i <= 'Z') ||
(i >= 'a' && i <= 'z') ||
(i >= '0' && i <= '9') ||
i == '-' ||
i == '.' ||
i == '_' ||
i == '~'
}
}

View File

@ -0,0 +1,107 @@
package httpbinding
import (
"encoding/base64"
"math"
"math/big"
"net/url"
"strconv"
)
// QueryValue is used to encode query key values
type QueryValue struct {
query url.Values
key string
append bool
}
// NewQueryValue creates a new QueryValue which enables encoding
// a query value into the given url.Values.
func NewQueryValue(query url.Values, key string, append bool) QueryValue {
return QueryValue{
query: query,
key: key,
append: append,
}
}
func (qv QueryValue) updateKey(value string) {
if qv.append {
qv.query.Add(qv.key, value)
} else {
qv.query.Set(qv.key, value)
}
}
// Blob encodes v as a base64 query string value
func (qv QueryValue) Blob(v []byte) {
encodeToString := base64.StdEncoding.EncodeToString(v)
qv.updateKey(encodeToString)
}
// Boolean encodes v as a query string value
func (qv QueryValue) Boolean(v bool) {
qv.updateKey(strconv.FormatBool(v))
}
// String encodes v as a query string value
func (qv QueryValue) String(v string) {
qv.updateKey(v)
}
// Byte encodes v as a query string value
func (qv QueryValue) Byte(v int8) {
qv.Long(int64(v))
}
// Short encodes v as a query string value
func (qv QueryValue) Short(v int16) {
qv.Long(int64(v))
}
// Integer encodes v as a query string value
func (qv QueryValue) Integer(v int32) {
qv.Long(int64(v))
}
// Long encodes v as a query string value
func (qv QueryValue) Long(v int64) {
qv.updateKey(strconv.FormatInt(v, 10))
}
// Float encodes v as a query string value
func (qv QueryValue) Float(v float32) {
qv.float(float64(v), 32)
}
// Double encodes v as a query string value
func (qv QueryValue) Double(v float64) {
qv.float(v, 64)
}
func (qv QueryValue) float(v float64, bitSize int) {
switch {
case math.IsNaN(v):
qv.String(floatNaN)
case math.IsInf(v, 1):
qv.String(floatInfinity)
case math.IsInf(v, -1):
qv.String(floatNegInfinity)
default:
qv.updateKey(strconv.FormatFloat(v, 'f', -1, bitSize))
}
}
// BigInteger encodes v as a query string value
func (qv QueryValue) BigInteger(v *big.Int) {
qv.updateKey(v.String())
}
// BigDecimal encodes v as a query string value
func (qv QueryValue) BigDecimal(v *big.Float) {
if i, accuracy := v.Int64(); accuracy == big.Exact {
qv.Long(i)
return
}
qv.updateKey(v.Text('e', -1))
}

View File

@ -0,0 +1,108 @@
package httpbinding
import (
"math"
"math/big"
"strconv"
"strings"
)
// URIValue is used to encode named URI parameters
type URIValue struct {
path, rawPath, buffer *[]byte
key string
}
func newURIValue(path *[]byte, rawPath *[]byte, buffer *[]byte, key string) URIValue {
return URIValue{path: path, rawPath: rawPath, buffer: buffer, key: key}
}
func (u URIValue) modifyURI(value string) (err error) {
*u.path, *u.buffer, err = replacePathElement(*u.path, *u.buffer, u.key, value, false)
*u.rawPath, *u.buffer, err = replacePathElement(*u.rawPath, *u.buffer, u.key, value, true)
return err
}
// Boolean encodes v as a URI string value
func (u URIValue) Boolean(v bool) error {
return u.modifyURI(strconv.FormatBool(v))
}
// String encodes v as a URI string value
func (u URIValue) String(v string) error {
return u.modifyURI(v)
}
// Byte encodes v as a URI string value
func (u URIValue) Byte(v int8) error {
return u.Long(int64(v))
}
// Short encodes v as a URI string value
func (u URIValue) Short(v int16) error {
return u.Long(int64(v))
}
// Integer encodes v as a URI string value
func (u URIValue) Integer(v int32) error {
return u.Long(int64(v))
}
// Long encodes v as a URI string value
func (u URIValue) Long(v int64) error {
return u.modifyURI(strconv.FormatInt(v, 10))
}
// Float encodes v as a query string value
func (u URIValue) Float(v float32) error {
return u.float(float64(v), 32)
}
// Double encodes v as a query string value
func (u URIValue) Double(v float64) error {
return u.float(v, 64)
}
func (u URIValue) float(v float64, bitSize int) error {
switch {
case math.IsNaN(v):
return u.String(floatNaN)
case math.IsInf(v, 1):
return u.String(floatInfinity)
case math.IsInf(v, -1):
return u.String(floatNegInfinity)
default:
return u.modifyURI(strconv.FormatFloat(v, 'f', -1, bitSize))
}
}
// BigInteger encodes v as a query string value
func (u URIValue) BigInteger(v *big.Int) error {
return u.modifyURI(v.String())
}
// BigDecimal encodes v as a query string value
func (u URIValue) BigDecimal(v *big.Float) error {
if i, accuracy := v.Int64(); accuracy == big.Exact {
return u.Long(i)
}
return u.modifyURI(v.Text('e', -1))
}
// SplitURI parses a Smithy HTTP binding trait URI
func SplitURI(uri string) (path, query string) {
queryStart := strings.IndexRune(uri, '?')
if queryStart == -1 {
path = uri
return path, query
}
path = uri[:queryStart]
if queryStart+1 >= len(uri) {
return path, query
}
query = uri[queryStart+1:]
return path, query
}

35
vendor/github.com/aws/smithy-go/encoding/json/array.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
package json
import (
"bytes"
)
// Array represent the encoding of a JSON Array
type Array struct {
w *bytes.Buffer
writeComma bool
scratch *[]byte
}
func newArray(w *bytes.Buffer, scratch *[]byte) *Array {
w.WriteRune(leftBracket)
return &Array{w: w, scratch: scratch}
}
// Value adds a new element to the JSON Array.
// Returns a Value type that is used to encode
// the array element.
func (a *Array) Value() Value {
if a.writeComma {
a.w.WriteRune(comma)
} else {
a.writeComma = true
}
return newValue(a.w, a.scratch)
}
// Close encodes the end of the JSON Array
func (a *Array) Close() {
a.w.WriteRune(rightBracket)
}

View File

@ -0,0 +1,15 @@
package json
const (
leftBrace = '{'
rightBrace = '}'
leftBracket = '['
rightBracket = ']'
comma = ','
quote = '"'
colon = ':'
null = "null"
)

View File

@ -0,0 +1,139 @@
package json
import (
"bytes"
"encoding/json"
"fmt"
"io"
)
// DiscardUnknownField discards unknown fields from decoder body.
// This function is useful while deserializing json body with additional
// unknown information that should be discarded.
func DiscardUnknownField(decoder *json.Decoder) error {
// This deliberately does not share logic with CollectUnknownField, even
// though it could, because if we were to delegate to that then we'd incur
// extra allocations and general memory usage.
v, err := decoder.Token()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if _, ok := v.(json.Delim); ok {
for decoder.More() {
err = DiscardUnknownField(decoder)
}
endToken, err := decoder.Token()
if err != nil {
return err
}
if _, ok := endToken.(json.Delim); !ok {
return fmt.Errorf("invalid JSON : expected json delimiter, found %T %v",
endToken, endToken)
}
}
return nil
}
// CollectUnknownField grabs the contents of unknown fields from the decoder body
// and returns them as a byte slice. This is useful for skipping unknown fields without
// completely discarding them.
func CollectUnknownField(decoder *json.Decoder) ([]byte, error) {
result, err := collectUnknownField(decoder)
if err != nil {
return nil, err
}
buff := bytes.NewBuffer(nil)
encoder := json.NewEncoder(buff)
if err := encoder.Encode(result); err != nil {
return nil, err
}
return buff.Bytes(), nil
}
func collectUnknownField(decoder *json.Decoder) (interface{}, error) {
// Grab the initial value. This could either be a concrete value like a string or a a
// delimiter.
token, err := decoder.Token()
if err == io.EOF {
return nil, nil
}
if err != nil {
return nil, err
}
// If it's an array or object, we'll need to recurse.
delim, ok := token.(json.Delim)
if ok {
var result interface{}
if delim == '{' {
result, err = collectUnknownObject(decoder)
if err != nil {
return nil, err
}
} else {
result, err = collectUnknownArray(decoder)
if err != nil {
return nil, err
}
}
// Discard the closing token. decoder.Token handles checking for matching delimiters
if _, err := decoder.Token(); err != nil {
return nil, err
}
return result, nil
}
return token, nil
}
func collectUnknownArray(decoder *json.Decoder) ([]interface{}, error) {
// We need to create an empty array here instead of a nil array, since by getting
// into this function at all we necessarily have seen a non-nil list.
array := []interface{}{}
for decoder.More() {
value, err := collectUnknownField(decoder)
if err != nil {
return nil, err
}
array = append(array, value)
}
return array, nil
}
func collectUnknownObject(decoder *json.Decoder) (map[string]interface{}, error) {
object := make(map[string]interface{})
for decoder.More() {
key, err := collectUnknownField(decoder)
if err != nil {
return nil, err
}
// Keys have to be strings, which is particularly important as the encoder
// won't except a map with interface{} keys
stringKey, ok := key.(string)
if !ok {
return nil, fmt.Errorf("expected string key, found %T", key)
}
value, err := collectUnknownField(decoder)
if err != nil {
return nil, err
}
object[stringKey] = value
}
return object, nil
}

View File

@ -0,0 +1,30 @@
package json
import (
"bytes"
)
// Encoder is JSON encoder that supports construction of JSON values
// using methods.
type Encoder struct {
w *bytes.Buffer
Value
}
// NewEncoder returns a new JSON encoder
func NewEncoder() *Encoder {
writer := bytes.NewBuffer(nil)
scratch := make([]byte, 64)
return &Encoder{w: writer, Value: newValue(writer, &scratch)}
}
// String returns the String output of the JSON encoder
func (e Encoder) String() string {
return e.w.String()
}
// Bytes returns the []byte slice of the JSON encoder
func (e Encoder) Bytes() []byte {
return e.w.Bytes()
}

198
vendor/github.com/aws/smithy-go/encoding/json/escape.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and modified from Go 1.8 stdlib's encoding/json/#safeSet
package json
import (
"bytes"
"unicode/utf8"
)
// safeSet holds the value true if the ASCII character with the given array
// position can be represented inside a JSON string without any further
// escaping.
//
// All values are true except for the ASCII control characters (0-31), the
// double quote ("), and the backslash character ("\").
var safeSet = [utf8.RuneSelf]bool{
' ': true,
'!': true,
'"': false,
'#': true,
'$': true,
'%': true,
'&': true,
'\'': true,
'(': true,
')': true,
'*': true,
'+': true,
',': true,
'-': true,
'.': true,
'/': true,
'0': true,
'1': true,
'2': true,
'3': true,
'4': true,
'5': true,
'6': true,
'7': true,
'8': true,
'9': true,
':': true,
';': true,
'<': true,
'=': true,
'>': true,
'?': true,
'@': true,
'A': true,
'B': true,
'C': true,
'D': true,
'E': true,
'F': true,
'G': true,
'H': true,
'I': true,
'J': true,
'K': true,
'L': true,
'M': true,
'N': true,
'O': true,
'P': true,
'Q': true,
'R': true,
'S': true,
'T': true,
'U': true,
'V': true,
'W': true,
'X': true,
'Y': true,
'Z': true,
'[': true,
'\\': false,
']': true,
'^': true,
'_': true,
'`': true,
'a': true,
'b': true,
'c': true,
'd': true,
'e': true,
'f': true,
'g': true,
'h': true,
'i': true,
'j': true,
'k': true,
'l': true,
'm': true,
'n': true,
'o': true,
'p': true,
'q': true,
'r': true,
's': true,
't': true,
'u': true,
'v': true,
'w': true,
'x': true,
'y': true,
'z': true,
'{': true,
'|': true,
'}': true,
'~': true,
'\u007f': true,
}
// copied from Go 1.8 stdlib's encoding/json/#hex
var hex = "0123456789abcdef"
// escapeStringBytes escapes and writes the passed in string bytes to the dst
// buffer
//
// Copied and modifed from Go 1.8 stdlib's encodeing/json/#encodeState.stringBytes
func escapeStringBytes(e *bytes.Buffer, s []byte) {
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if safeSet[b] {
i++
continue
}
if start < i {
e.Write(s[start:i])
}
switch b {
case '\\', '"':
e.WriteByte('\\')
e.WriteByte(b)
case '\n':
e.WriteByte('\\')
e.WriteByte('n')
case '\r':
e.WriteByte('\\')
e.WriteByte('r')
case '\t':
e.WriteByte('\\')
e.WriteByte('t')
default:
// This encodes bytes < 0x20 except for \t, \n and \r.
// If escapeHTML is set, it also escapes <, >, and &
// because they can lead to security holes when
// user-controlled strings are rendered into JSON
// and served to some browsers.
e.WriteString(`\u00`)
e.WriteByte(hex[b>>4])
e.WriteByte(hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRune(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
e.Write(s[start:i])
}
e.WriteString(`\ufffd`)
i += size
start = i
continue
}
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
if c == '\u2028' || c == '\u2029' {
if start < i {
e.Write(s[start:i])
}
e.WriteString(`\u202`)
e.WriteByte(hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
e.Write(s[start:])
}
e.WriteByte('"')
}

View File

@ -0,0 +1,42 @@
package json
import (
"bytes"
)
// Object represents the encoding of a JSON Object type
type Object struct {
w *bytes.Buffer
writeComma bool
scratch *[]byte
}
func newObject(w *bytes.Buffer, scratch *[]byte) *Object {
w.WriteRune(leftBrace)
return &Object{w: w, scratch: scratch}
}
func (o *Object) writeKey(key string) {
o.w.WriteRune(quote)
o.w.Write([]byte(key))
o.w.WriteRune(quote)
o.w.WriteRune(colon)
}
// Key adds the given named key to the JSON object.
// Returns a Value encoder that should be used to encode
// a JSON value type.
func (o *Object) Key(name string) Value {
if o.writeComma {
o.w.WriteRune(comma)
} else {
o.writeComma = true
}
o.writeKey(name)
return newValue(o.w, o.scratch)
}
// Close encodes the end of the JSON Object
func (o *Object) Close() {
o.w.WriteRune(rightBrace)
}

149
vendor/github.com/aws/smithy-go/encoding/json/value.go generated vendored Normal file
View File

@ -0,0 +1,149 @@
package json
import (
"bytes"
"encoding/base64"
"math/big"
"strconv"
"github.com/aws/smithy-go/encoding"
)
// Value represents a JSON Value type
// JSON Value types: Object, Array, String, Number, Boolean, and Null
type Value struct {
w *bytes.Buffer
scratch *[]byte
}
// newValue returns a new Value encoder
func newValue(w *bytes.Buffer, scratch *[]byte) Value {
return Value{w: w, scratch: scratch}
}
// String encodes v as a JSON string
func (jv Value) String(v string) {
escapeStringBytes(jv.w, []byte(v))
}
// Byte encodes v as a JSON number
func (jv Value) Byte(v int8) {
jv.Long(int64(v))
}
// Short encodes v as a JSON number
func (jv Value) Short(v int16) {
jv.Long(int64(v))
}
// Integer encodes v as a JSON number
func (jv Value) Integer(v int32) {
jv.Long(int64(v))
}
// Long encodes v as a JSON number
func (jv Value) Long(v int64) {
*jv.scratch = strconv.AppendInt((*jv.scratch)[:0], v, 10)
jv.w.Write(*jv.scratch)
}
// ULong encodes v as a JSON number
func (jv Value) ULong(v uint64) {
*jv.scratch = strconv.AppendUint((*jv.scratch)[:0], v, 10)
jv.w.Write(*jv.scratch)
}
// Float encodes v as a JSON number
func (jv Value) Float(v float32) {
jv.float(float64(v), 32)
}
// Double encodes v as a JSON number
func (jv Value) Double(v float64) {
jv.float(v, 64)
}
func (jv Value) float(v float64, bits int) {
*jv.scratch = encoding.EncodeFloat((*jv.scratch)[:0], v, bits)
jv.w.Write(*jv.scratch)
}
// Boolean encodes v as a JSON boolean
func (jv Value) Boolean(v bool) {
*jv.scratch = strconv.AppendBool((*jv.scratch)[:0], v)
jv.w.Write(*jv.scratch)
}
// Base64EncodeBytes writes v as a base64 value in JSON string
func (jv Value) Base64EncodeBytes(v []byte) {
encodeByteSlice(jv.w, (*jv.scratch)[:0], v)
}
// Write writes v directly to the JSON document
func (jv Value) Write(v []byte) {
jv.w.Write(v)
}
// Array returns a new Array encoder
func (jv Value) Array() *Array {
return newArray(jv.w, jv.scratch)
}
// Object returns a new Object encoder
func (jv Value) Object() *Object {
return newObject(jv.w, jv.scratch)
}
// Null encodes a null JSON value
func (jv Value) Null() {
jv.w.WriteString(null)
}
// BigInteger encodes v as JSON value
func (jv Value) BigInteger(v *big.Int) {
jv.w.Write([]byte(v.Text(10)))
}
// BigDecimal encodes v as JSON value
func (jv Value) BigDecimal(v *big.Float) {
if i, accuracy := v.Int64(); accuracy == big.Exact {
jv.Long(i)
return
}
// TODO: Should this try to match ES6 ToString similar to stdlib JSON?
jv.w.Write([]byte(v.Text('e', -1)))
}
// Based on encoding/json encodeByteSlice from the Go Standard Library
// https://golang.org/src/encoding/json/encode.go
func encodeByteSlice(w *bytes.Buffer, scratch []byte, v []byte) {
if v == nil {
w.WriteString(null)
return
}
w.WriteRune(quote)
encodedLen := base64.StdEncoding.EncodedLen(len(v))
if encodedLen <= len(scratch) {
// If the encoded bytes fit in e.scratch, avoid an extra
// allocation and use the cheaper Encoding.Encode.
dst := scratch[:encodedLen]
base64.StdEncoding.Encode(dst, v)
w.Write(dst)
} else if encodedLen <= 1024 {
// The encoded bytes are short enough to allocate for, and
// Encoding.Encode is still cheaper.
dst := make([]byte, encodedLen)
base64.StdEncoding.Encode(dst, v)
w.Write(dst)
} else {
// The encoded bytes are too long to cheaply allocate, and
// Encoding.Encode is no longer noticeably cheaper.
enc := base64.NewEncoder(base64.StdEncoding, w)
enc.Write(v)
enc.Close()
}
w.WriteRune(quote)
}

49
vendor/github.com/aws/smithy-go/encoding/xml/array.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
package xml
// arrayMemberWrapper is the default member wrapper tag name for XML Array type
var arrayMemberWrapper = StartElement{
Name: Name{Local: "member"},
}
// Array represents the encoding of a XML array type
type Array struct {
w writer
scratch *[]byte
// member start element is the array member wrapper start element
memberStartElement StartElement
// isFlattened indicates if the array is a flattened array.
isFlattened bool
}
// newArray returns an array encoder.
// It also takes in the member start element, array start element.
// It takes in a isFlattened bool, indicating that an array is flattened array.
//
// A wrapped array ["value1", "value2"] is represented as
// `<List><member>value1</member><member>value2</member></List>`.
// A flattened array `someList: ["value1", "value2"]` is represented as
// `<someList>value1</someList><someList>value2</someList>`.
func newArray(w writer, scratch *[]byte, memberStartElement StartElement, arrayStartElement StartElement, isFlattened bool) *Array {
var memberWrapper = memberStartElement
if isFlattened {
memberWrapper = arrayStartElement
}
return &Array{
w: w,
scratch: scratch,
memberStartElement: memberWrapper,
isFlattened: isFlattened,
}
}
// Member adds a new member to the XML array.
// It returns a Value encoder.
func (a *Array) Member() Value {
v := newValue(a.w, a.scratch, a.memberStartElement)
v.isFlattened = a.isFlattened
return v
}

View File

@ -0,0 +1,10 @@
package xml
const (
leftAngleBracket = '<'
rightAngleBracket = '>'
forwardSlash = '/'
colon = ':'
equals = '='
quote = '"'
)

49
vendor/github.com/aws/smithy-go/encoding/xml/doc.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
/*
Package xml holds the XMl encoder utility. This utility is written in accordance to our design to delegate to
shape serializer function in which a xml.Value will be passed around.
Resources followed: https://awslabs.github.io/smithy/1.0/spec/core/xml-traits.html#
Member Element
Member element should be used to encode xml shapes into xml elements except for flattened xml shapes. Member element
write their own element start tag. These elements should always be closed.
Flattened Element
Flattened element should be used to encode shapes marked with flattened trait into xml elements. Flattened element
do not write a start tag, and thus should not be closed.
Simple types encoding
All simple type methods on value such as String(), Long() etc; auto close the associated member element.
Array
Array returns the collection encoder. It has two modes, wrapped and flattened encoding.
Wrapped arrays have two methods Array() and ArrayWithCustomName() which facilitate array member wrapping.
By default, a wrapped array members are wrapped with `member` named start element.
<wrappedArray><member>apple</member><member>tree</member></wrappedArray>
Flattened arrays rely on Value being marked as flattened.
If a shape is marked as flattened, Array() will use the shape element name as wrapper for array elements.
<flattenedAarray>apple</flattenedArray><flattenedArray>tree</flattenedArray>
Map
Map is the map encoder. It has two modes, wrapped and flattened encoding.
Wrapped map has Array() method, which facilitate map member wrapping.
By default, a wrapped map members are wrapped with `entry` named start element.
<wrappedMap><entry><Key>apple</Key><Value>tree</Value></entry><entry><Key>snow</Key><Value>ice</Value></entry></wrappedMap>
Flattened map rely on Value being marked as flattened.
If a shape is marked as flattened, Map() will use the shape element name as wrapper for map entry elements.
<flattenedMap><Key>apple</Key><Value>tree</Value></flattenedMap><flattenedMap><Key>snow</Key><Value>ice</Value></flattenedMap>
*/
package xml

View File

@ -0,0 +1,91 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and modified from Go 1.14 stdlib's encoding/xml
package xml
// A Name represents an XML name (Local) annotated
// with a name space identifier (Space).
// In tokens returned by Decoder.Token, the Space identifier
// is given as a canonical URL, not the short prefix used
// in the document being parsed.
type Name struct {
Space, Local string
}
// An Attr represents an attribute in an XML element (Name=Value).
type Attr struct {
Name Name
Value string
}
/*
NewAttribute returns a pointer to an attribute.
It takes in a local name aka attribute name, and value
representing the attribute value.
*/
func NewAttribute(local, value string) Attr {
return Attr{
Name: Name{
Local: local,
},
Value: value,
}
}
/*
NewNamespaceAttribute returns a pointer to an attribute.
It takes in a local name aka attribute name, and value
representing the attribute value.
NewNamespaceAttribute appends `xmlns:` in front of namespace
prefix.
For creating a name space attribute representing
`xmlns:prefix="http://example.com`, the breakdown would be:
local = "prefix"
value = "http://example.com"
*/
func NewNamespaceAttribute(local, value string) Attr {
attr := NewAttribute(local, value)
// default name space identifier
attr.Name.Space = "xmlns"
return attr
}
// A StartElement represents an XML start element.
type StartElement struct {
Name Name
Attr []Attr
}
// Copy creates a new copy of StartElement.
func (e StartElement) Copy() StartElement {
attrs := make([]Attr, len(e.Attr))
copy(attrs, e.Attr)
e.Attr = attrs
return e
}
// End returns the corresponding XML end element.
func (e StartElement) End() EndElement {
return EndElement{e.Name}
}
// returns true if start element local name is empty
func (e StartElement) isZero() bool {
return len(e.Name.Local) == 0
}
// An EndElement represents an XML end element.
type EndElement struct {
Name Name
}
// returns true if end element local name is empty
func (e EndElement) isZero() bool {
return len(e.Name.Local) == 0
}

View File

@ -0,0 +1,51 @@
package xml
// writer interface used by the xml encoder to write an encoded xml
// document in a writer.
type writer interface {
// Write takes in a byte slice and returns number of bytes written and error
Write(p []byte) (n int, err error)
// WriteRune takes in a rune and returns number of bytes written and error
WriteRune(r rune) (n int, err error)
// WriteString takes in a string and returns number of bytes written and error
WriteString(s string) (n int, err error)
// String method returns a string
String() string
// Bytes return a byte slice.
Bytes() []byte
}
// Encoder is an XML encoder that supports construction of XML values
// using methods. The encoder takes in a writer and maintains a scratch buffer.
type Encoder struct {
w writer
scratch *[]byte
}
// NewEncoder returns an XML encoder
func NewEncoder(w writer) *Encoder {
scratch := make([]byte, 64)
return &Encoder{w: w, scratch: &scratch}
}
// String returns the string output of the XML encoder
func (e Encoder) String() string {
return e.w.String()
}
// Bytes returns the []byte slice of the XML encoder
func (e Encoder) Bytes() []byte {
return e.w.Bytes()
}
// RootElement builds a root element encoding
// It writes it's start element tag. The value should be closed.
func (e Encoder) RootElement(element StartElement) Value {
return newValue(e.w, e.scratch, element)
}

View File

@ -0,0 +1,51 @@
package xml
import (
"encoding/xml"
"fmt"
"io"
)
// ErrorComponents represents the error response fields
// that will be deserialized from an xml error response body
type ErrorComponents struct {
Code string
Message string
}
// GetErrorResponseComponents returns the error fields from an xml error response body
func GetErrorResponseComponents(r io.Reader, noErrorWrapping bool) (ErrorComponents, error) {
if noErrorWrapping {
var errResponse noWrappedErrorResponse
if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF {
return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err)
}
return ErrorComponents{
Code: errResponse.Code,
Message: errResponse.Message,
}, nil
}
var errResponse wrappedErrorResponse
if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF {
return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err)
}
return ErrorComponents{
Code: errResponse.Code,
Message: errResponse.Message,
}, nil
}
// noWrappedErrorResponse represents the error response body with
// no internal <Error></Error wrapping
type noWrappedErrorResponse struct {
Code string `xml:"Code"`
Message string `xml:"Message"`
}
// wrappedErrorResponse represents the error response body
// wrapped within <Error>...</Error>
type wrappedErrorResponse struct {
Code string `xml:"Error>Code"`
Message string `xml:"Error>Message"`
}

137
vendor/github.com/aws/smithy-go/encoding/xml/escape.go generated vendored Normal file
View File

@ -0,0 +1,137 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied and modified from Go 1.14 stdlib's encoding/xml
package xml
import (
"unicode/utf8"
)
// Copied from Go 1.14 stdlib's encoding/xml
var (
escQuot = []byte("&#34;") // shorter than "&quot;"
escApos = []byte("&#39;") // shorter than "&apos;"
escAmp = []byte("&amp;")
escLT = []byte("&lt;")
escGT = []byte("&gt;")
escTab = []byte("&#x9;")
escNL = []byte("&#xA;")
escCR = []byte("&#xD;")
escFFFD = []byte("\uFFFD") // Unicode replacement character
// Additional Escapes
escNextLine = []byte("&#x85;")
escLS = []byte("&#x2028;")
)
// Decide whether the given rune is in the XML Character Range, per
// the Char production of https://www.xml.com/axml/testaxml.htm,
// Section 2.2 Characters.
func isInCharacterRange(r rune) (inrange bool) {
return r == 0x09 ||
r == 0x0A ||
r == 0x0D ||
r >= 0x20 && r <= 0xD7FF ||
r >= 0xE000 && r <= 0xFFFD ||
r >= 0x10000 && r <= 0x10FFFF
}
// TODO: When do we need to escape the string?
// Based on encoding/xml escapeString from the Go Standard Library.
// https://golang.org/src/encoding/xml/xml.go
func escapeString(e writer, s string) {
var esc []byte
last := 0
for i := 0; i < len(s); {
r, width := utf8.DecodeRuneInString(s[i:])
i += width
switch r {
case '"':
esc = escQuot
case '\'':
esc = escApos
case '&':
esc = escAmp
case '<':
esc = escLT
case '>':
esc = escGT
case '\t':
esc = escTab
case '\n':
esc = escNL
case '\r':
esc = escCR
case '\u0085':
// Not escaped by stdlib
esc = escNextLine
case '\u2028':
// Not escaped by stdlib
esc = escLS
default:
if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
esc = escFFFD
break
}
continue
}
e.WriteString(s[last : i-width])
e.Write(esc)
last = i
}
e.WriteString(s[last:])
}
// escapeText writes to w the properly escaped XML equivalent
// of the plain text data s. If escapeNewline is true, newline
// characters will be escaped.
//
// Based on encoding/xml escapeText from the Go Standard Library.
// https://golang.org/src/encoding/xml/xml.go
func escapeText(e writer, s []byte) {
var esc []byte
last := 0
for i := 0; i < len(s); {
r, width := utf8.DecodeRune(s[i:])
i += width
switch r {
case '"':
esc = escQuot
case '\'':
esc = escApos
case '&':
esc = escAmp
case '<':
esc = escLT
case '>':
esc = escGT
case '\t':
esc = escTab
case '\n':
// This always escapes newline, which is different than stdlib's optional
// escape of new line.
esc = escNL
case '\r':
esc = escCR
case '\u0085':
// Not escaped by stdlib
esc = escNextLine
case '\u2028':
// Not escaped by stdlib
esc = escLS
default:
if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
esc = escFFFD
break
}
continue
}
e.Write(s[last : i-width])
e.Write(esc)
last = i
}
e.Write(s[last:])
}

53
vendor/github.com/aws/smithy-go/encoding/xml/map.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
package xml
// mapEntryWrapper is the default member wrapper start element for XML Map entry
var mapEntryWrapper = StartElement{
Name: Name{Local: "entry"},
}
// Map represents the encoding of a XML map type
type Map struct {
w writer
scratch *[]byte
// member start element is the map entry wrapper start element
memberStartElement StartElement
// isFlattened returns true if the map is a flattened map
isFlattened bool
}
// newMap returns a map encoder which sets the default map
// entry wrapper to `entry`.
//
// A map `someMap : {{key:"abc", value:"123"}}` is represented as
// `<someMap><entry><key>abc<key><value>123</value></entry></someMap>`.
func newMap(w writer, scratch *[]byte) *Map {
return &Map{
w: w,
scratch: scratch,
memberStartElement: mapEntryWrapper,
}
}
// newFlattenedMap returns a map encoder which sets the map
// entry wrapper to the passed in memberWrapper`.
//
// A flattened map `someMap : {{key:"abc", value:"123"}}` is represented as
// `<someMap><key>abc<key><value>123</value></someMap>`.
func newFlattenedMap(w writer, scratch *[]byte, memberWrapper StartElement) *Map {
return &Map{
w: w,
scratch: scratch,
memberStartElement: memberWrapper,
isFlattened: true,
}
}
// Entry returns a Value encoder with map's element.
// It writes the member wrapper start tag for each entry.
func (m *Map) Entry() Value {
v := newValue(m.w, m.scratch, m.memberStartElement)
v.isFlattened = m.isFlattened
return v
}

302
vendor/github.com/aws/smithy-go/encoding/xml/value.go generated vendored Normal file
View File

@ -0,0 +1,302 @@
package xml
import (
"encoding/base64"
"fmt"
"math/big"
"strconv"
"github.com/aws/smithy-go/encoding"
)
// Value represents an XML Value type
// XML Value types: Object, Array, Map, String, Number, Boolean.
type Value struct {
w writer
scratch *[]byte
// xml start element is the associated start element for the Value
startElement StartElement
// indicates if the Value represents a flattened shape
isFlattened bool
}
// newFlattenedValue returns a Value encoder. newFlattenedValue does NOT write the start element tag
func newFlattenedValue(w writer, scratch *[]byte, startElement StartElement) Value {
return Value{
w: w,
scratch: scratch,
startElement: startElement,
}
}
// newValue writes the start element xml tag and returns a Value
func newValue(w writer, scratch *[]byte, startElement StartElement) Value {
writeStartElement(w, startElement)
return Value{w: w, scratch: scratch, startElement: startElement}
}
// writeStartElement takes in a start element and writes it.
// It handles namespace, attributes in start element.
func writeStartElement(w writer, el StartElement) error {
if el.isZero() {
return fmt.Errorf("xml start element cannot be nil")
}
w.WriteRune(leftAngleBracket)
if len(el.Name.Space) != 0 {
escapeString(w, el.Name.Space)
w.WriteRune(colon)
}
escapeString(w, el.Name.Local)
for _, attr := range el.Attr {
w.WriteRune(' ')
writeAttribute(w, &attr)
}
w.WriteRune(rightAngleBracket)
return nil
}
// writeAttribute writes an attribute from a provided Attribute
// For a namespace attribute, the attr.Name.Space must be defined as "xmlns".
// https://www.w3.org/TR/REC-xml-names/#NT-DefaultAttName
func writeAttribute(w writer, attr *Attr) {
// if local, space both are not empty
if len(attr.Name.Space) != 0 && len(attr.Name.Local) != 0 {
escapeString(w, attr.Name.Space)
w.WriteRune(colon)
}
// if prefix is empty, the default `xmlns` space should be used as prefix.
if len(attr.Name.Local) == 0 {
attr.Name.Local = attr.Name.Space
}
escapeString(w, attr.Name.Local)
w.WriteRune(equals)
w.WriteRune(quote)
escapeString(w, attr.Value)
w.WriteRune(quote)
}
// writeEndElement takes in a end element and writes it.
func writeEndElement(w writer, el EndElement) error {
if el.isZero() {
return fmt.Errorf("xml end element cannot be nil")
}
w.WriteRune(leftAngleBracket)
w.WriteRune(forwardSlash)
if len(el.Name.Space) != 0 {
escapeString(w, el.Name.Space)
w.WriteRune(colon)
}
escapeString(w, el.Name.Local)
w.WriteRune(rightAngleBracket)
return nil
}
// String encodes v as a XML string.
// It will auto close the parent xml element tag.
func (xv Value) String(v string) {
escapeString(xv.w, v)
xv.Close()
}
// Byte encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Byte(v int8) {
xv.Long(int64(v))
}
// Short encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Short(v int16) {
xv.Long(int64(v))
}
// Integer encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Integer(v int32) {
xv.Long(int64(v))
}
// Long encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Long(v int64) {
*xv.scratch = strconv.AppendInt((*xv.scratch)[:0], v, 10)
xv.w.Write(*xv.scratch)
xv.Close()
}
// Float encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Float(v float32) {
xv.float(float64(v), 32)
xv.Close()
}
// Double encodes v as a XML number.
// It will auto close the parent xml element tag.
func (xv Value) Double(v float64) {
xv.float(v, 64)
xv.Close()
}
func (xv Value) float(v float64, bits int) {
*xv.scratch = encoding.EncodeFloat((*xv.scratch)[:0], v, bits)
xv.w.Write(*xv.scratch)
}
// Boolean encodes v as a XML boolean.
// It will auto close the parent xml element tag.
func (xv Value) Boolean(v bool) {
*xv.scratch = strconv.AppendBool((*xv.scratch)[:0], v)
xv.w.Write(*xv.scratch)
xv.Close()
}
// Base64EncodeBytes writes v as a base64 value in XML string.
// It will auto close the parent xml element tag.
func (xv Value) Base64EncodeBytes(v []byte) {
encodeByteSlice(xv.w, (*xv.scratch)[:0], v)
xv.Close()
}
// BigInteger encodes v big.Int as XML value.
// It will auto close the parent xml element tag.
func (xv Value) BigInteger(v *big.Int) {
xv.w.Write([]byte(v.Text(10)))
xv.Close()
}
// BigDecimal encodes v big.Float as XML value.
// It will auto close the parent xml element tag.
func (xv Value) BigDecimal(v *big.Float) {
if i, accuracy := v.Int64(); accuracy == big.Exact {
xv.Long(i)
return
}
xv.w.Write([]byte(v.Text('e', -1)))
xv.Close()
}
// Write writes v directly to the xml document
// if escapeXMLText is set to true, write will escape text.
// It will auto close the parent xml element tag.
func (xv Value) Write(v []byte, escapeXMLText bool) {
// escape and write xml text
if escapeXMLText {
escapeText(xv.w, v)
} else {
// write xml directly
xv.w.Write(v)
}
xv.Close()
}
// MemberElement does member element encoding. It returns a Value.
// Member Element method should be used for all shapes except flattened shapes.
//
// A call to MemberElement will write nested element tags directly using the
// provided start element. The value returned by MemberElement should be closed.
func (xv Value) MemberElement(element StartElement) Value {
return newValue(xv.w, xv.scratch, element)
}
// FlattenedElement returns flattened element encoding. It returns a Value.
// This method should be used for flattened shapes.
//
// Unlike MemberElement, flattened element will NOT write element tags
// directly for the associated start element.
//
// The value returned by the FlattenedElement does not need to be closed.
func (xv Value) FlattenedElement(element StartElement) Value {
v := newFlattenedValue(xv.w, xv.scratch, element)
v.isFlattened = true
return v
}
// Array returns an array encoder. By default, the members of array are
// wrapped with `<member>` element tag.
// If value is marked as flattened, the start element is used to wrap the members instead of
// the `<member>` element.
func (xv Value) Array() *Array {
return newArray(xv.w, xv.scratch, arrayMemberWrapper, xv.startElement, xv.isFlattened)
}
/*
ArrayWithCustomName returns an array encoder.
It takes named start element as an argument, the named start element will used to wrap xml array entries.
for eg, `<someList><customName>entry1</customName></someList>`
Here `customName` named start element will be wrapped on each array member.
*/
func (xv Value) ArrayWithCustomName(element StartElement) *Array {
return newArray(xv.w, xv.scratch, element, xv.startElement, xv.isFlattened)
}
/*
Map returns a map encoder. By default, the map entries are
wrapped with `<entry>` element tag.
If value is marked as flattened, the start element is used to wrap the entry instead of
the `<member>` element.
*/
func (xv Value) Map() *Map {
// flattened map
if xv.isFlattened {
return newFlattenedMap(xv.w, xv.scratch, xv.startElement)
}
// un-flattened map
return newMap(xv.w, xv.scratch)
}
// encodeByteSlice is modified copy of json encoder's encodeByteSlice.
// It is used to base64 encode a byte slice.
func encodeByteSlice(w writer, scratch []byte, v []byte) {
if v == nil {
return
}
encodedLen := base64.StdEncoding.EncodedLen(len(v))
if encodedLen <= len(scratch) {
// If the encoded bytes fit in e.scratch, avoid an extra
// allocation and use the cheaper Encoding.Encode.
dst := scratch[:encodedLen]
base64.StdEncoding.Encode(dst, v)
w.Write(dst)
} else if encodedLen <= 1024 {
// The encoded bytes are short enough to allocate for, and
// Encoding.Encode is still cheaper.
dst := make([]byte, encodedLen)
base64.StdEncoding.Encode(dst, v)
w.Write(dst)
} else {
// The encoded bytes are too long to cheaply allocate, and
// Encoding.Encode is no longer noticeably cheaper.
enc := base64.NewEncoder(base64.StdEncoding, w)
enc.Write(v)
enc.Close()
}
}
// IsFlattened returns true if value is for flattened shape.
func (xv Value) IsFlattened() bool {
return xv.isFlattened
}
// Close closes the value.
func (xv Value) Close() {
writeEndElement(xv.w, xv.startElement.End())
}

View File

@ -0,0 +1,154 @@
package xml
import (
"encoding/xml"
"fmt"
"strings"
)
// NodeDecoder is a XML decoder wrapper that is responsible to decoding
// a single XML Node element and it's nested member elements. This wrapper decoder
// takes in the start element of the top level node being decoded.
type NodeDecoder struct {
Decoder *xml.Decoder
StartEl xml.StartElement
}
// WrapNodeDecoder returns an initialized XMLNodeDecoder
func WrapNodeDecoder(decoder *xml.Decoder, startEl xml.StartElement) NodeDecoder {
return NodeDecoder{
Decoder: decoder,
StartEl: startEl,
}
}
// Token on a Node Decoder returns a xml StartElement. It returns a boolean that indicates the
// a token is the node decoder's end node token; and an error which indicates any error
// that occurred while retrieving the start element
func (d NodeDecoder) Token() (t xml.StartElement, done bool, err error) {
for {
token, e := d.Decoder.Token()
if e != nil {
return t, done, e
}
// check if we reach end of the node being decoded
if el, ok := token.(xml.EndElement); ok {
return t, el == d.StartEl.End(), err
}
if t, ok := token.(xml.StartElement); ok {
return restoreAttrNamespaces(t), false, err
}
// skip token if it is a comment or preamble or empty space value due to indentation
// or if it's a value and is not expected
}
}
// restoreAttrNamespaces update XML attributes to restore the short namespaces found within
// the raw XML document.
func restoreAttrNamespaces(node xml.StartElement) xml.StartElement {
if len(node.Attr) == 0 {
return node
}
// Generate a mapping of XML namespace values to their short names.
ns := map[string]string{}
for _, a := range node.Attr {
if a.Name.Space == "xmlns" {
ns[a.Value] = a.Name.Local
break
}
}
for i, a := range node.Attr {
if a.Name.Space == "xmlns" {
continue
}
// By default, xml.Decoder will fully resolve these namespaces. So if you had <foo xmlns:bar=baz bar:bin=hi/>
// then by default the second attribute would have the `Name.Space` resolved to `baz`. But we need it to
// continue to resolve as `bar` so we can easily identify it later on.
if v, ok := ns[node.Attr[i].Name.Space]; ok {
node.Attr[i].Name.Space = v
}
}
return node
}
// GetElement looks for the given tag name at the current level, and returns the element if found, and
// skipping over non-matching elements. Returns an error if the node is not found, or if an error occurs while walking
// the document.
func (d NodeDecoder) GetElement(name string) (t xml.StartElement, err error) {
for {
token, done, err := d.Token()
if err != nil {
return t, err
}
if done {
return t, fmt.Errorf("%s node not found", name)
}
switch {
case strings.EqualFold(name, token.Name.Local):
return token, nil
default:
err = d.Decoder.Skip()
if err != nil {
return t, err
}
}
}
}
// Value provides an abstraction to retrieve char data value within an xml element.
// The method will return an error if it encounters a nested xml element instead of char data.
// This method should only be used to retrieve simple type or blob shape values as []byte.
func (d NodeDecoder) Value() (c []byte, err error) {
t, e := d.Decoder.Token()
if e != nil {
return c, e
}
endElement := d.StartEl.End()
switch ev := t.(type) {
case xml.CharData:
c = ev.Copy()
case xml.EndElement: // end tag or self-closing
if ev == endElement {
return []byte{}, err
}
return c, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t)
default:
return c, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t)
}
t, e = d.Decoder.Token()
if e != nil {
return c, e
}
if ev, ok := t.(xml.EndElement); ok {
if ev == endElement {
return c, err
}
}
return c, fmt.Errorf("expected end element %v, got %T type %v instead", endElement, t, t)
}
// FetchRootElement takes in a decoder and returns the first start element within the xml body.
// This function is useful in fetching the start element of an XML response and ignore the
// comments and preamble
func FetchRootElement(decoder *xml.Decoder) (startElement xml.StartElement, err error) {
for {
t, e := decoder.Token()
if e != nil {
return startElement, e
}
if startElement, ok := t.(xml.StartElement); ok {
return startElement, err
}
}
}