Move to vendor
Signed-off-by: Olivier Gambier <olivier@docker.com>
This commit is contained in:
parent
c8d8e7e357
commit
77e69b9cf3
1268 changed files with 34 additions and 24 deletions
592
vendor/google.golang.org/api/googleapi/googleapi.go
generated
vendored
Normal file
592
vendor/google.golang.org/api/googleapi/googleapi.go
generated
vendored
Normal file
|
@ -0,0 +1,592 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package googleapi contains the common code shared by all Google API
|
||||
// libraries.
|
||||
package googleapi // import "google.golang.org/api/googleapi"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
"google.golang.org/api/googleapi/internal/uritemplates"
|
||||
)
|
||||
|
||||
// ContentTyper is an interface for Readers which know (or would like
|
||||
// to override) their Content-Type. If a media body doesn't implement
|
||||
// ContentTyper, the type is sniffed from the content using
|
||||
// http.DetectContentType.
|
||||
type ContentTyper interface {
|
||||
ContentType() string
|
||||
}
|
||||
|
||||
// A SizeReaderAt is a ReaderAt with a Size method.
|
||||
// An io.SectionReader implements SizeReaderAt.
|
||||
type SizeReaderAt interface {
|
||||
io.ReaderAt
|
||||
Size() int64
|
||||
}
|
||||
|
||||
// ServerResponse is embedded in each Do response and
|
||||
// provides the HTTP status code and header sent by the server.
|
||||
type ServerResponse struct {
|
||||
// HTTPStatusCode is the server's response status code.
|
||||
// When using a resource method's Do call, this will always be in the 2xx range.
|
||||
HTTPStatusCode int
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
const (
|
||||
Version = "0.5"
|
||||
|
||||
// statusResumeIncomplete is the code returned by the Google uploader when the transfer is not yet complete.
|
||||
statusResumeIncomplete = 308
|
||||
|
||||
// UserAgent is the header string used to identify this package.
|
||||
UserAgent = "google-api-go-client/" + Version
|
||||
|
||||
// uploadPause determines the delay between failed upload attempts
|
||||
uploadPause = 1 * time.Second
|
||||
)
|
||||
|
||||
// Error contains an error response from the server.
|
||||
type Error struct {
|
||||
// Code is the HTTP response status code and will always be populated.
|
||||
Code int `json:"code"`
|
||||
// Message is the server response message and is only populated when
|
||||
// explicitly referenced by the JSON server response.
|
||||
Message string `json:"message"`
|
||||
// Body is the raw response returned by the server.
|
||||
// It is often but not always JSON, depending on how the request fails.
|
||||
Body string
|
||||
// Header contains the response header fields from the server.
|
||||
Header http.Header
|
||||
|
||||
Errors []ErrorItem
|
||||
}
|
||||
|
||||
// ErrorItem is a detailed error code & message from the Google API frontend.
|
||||
type ErrorItem struct {
|
||||
// Reason is the typed error code. For example: "some_example".
|
||||
Reason string `json:"reason"`
|
||||
// Message is the human-readable description of the error.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if len(e.Errors) == 0 && e.Message == "" {
|
||||
return fmt.Sprintf("googleapi: got HTTP response code %d with body: %v", e.Code, e.Body)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "googleapi: Error %d: ", e.Code)
|
||||
if e.Message != "" {
|
||||
fmt.Fprintf(&buf, "%s", e.Message)
|
||||
}
|
||||
if len(e.Errors) == 0 {
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
if len(e.Errors) == 1 && e.Errors[0].Message == e.Message {
|
||||
fmt.Fprintf(&buf, ", %s", e.Errors[0].Reason)
|
||||
return buf.String()
|
||||
}
|
||||
fmt.Fprintln(&buf, "\nMore details:")
|
||||
for _, v := range e.Errors {
|
||||
fmt.Fprintf(&buf, "Reason: %s, Message: %s\n", v.Reason, v.Message)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type errorReply struct {
|
||||
Error *Error `json:"error"`
|
||||
}
|
||||
|
||||
// CheckResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx.
|
||||
func CheckResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err == nil {
|
||||
jerr := new(errorReply)
|
||||
err = json.Unmarshal(slurp, jerr)
|
||||
if err == nil && jerr.Error != nil {
|
||||
if jerr.Error.Code == 0 {
|
||||
jerr.Error.Code = res.StatusCode
|
||||
}
|
||||
jerr.Error.Body = string(slurp)
|
||||
return jerr.Error
|
||||
}
|
||||
}
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
Header: res.Header,
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotModified reports whether err is the result of the
|
||||
// server replying with http.StatusNotModified.
|
||||
// Such error values are sometimes returned by "Do" methods
|
||||
// on calls when If-None-Match is used.
|
||||
func IsNotModified(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ae, ok := err.(*Error)
|
||||
return ok && ae.Code == http.StatusNotModified
|
||||
}
|
||||
|
||||
// CheckMediaResponse returns an error (of type *Error) if the response
|
||||
// status code is not 2xx. Unlike CheckResponse it does not assume the
|
||||
// body is a JSON error document.
|
||||
func CheckMediaResponse(res *http.Response) error {
|
||||
if res.StatusCode >= 200 && res.StatusCode <= 299 {
|
||||
return nil
|
||||
}
|
||||
slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
res.Body.Close()
|
||||
return &Error{
|
||||
Code: res.StatusCode,
|
||||
Body: string(slurp),
|
||||
}
|
||||
}
|
||||
|
||||
type MarshalStyle bool
|
||||
|
||||
var WithDataWrapper = MarshalStyle(true)
|
||||
var WithoutDataWrapper = MarshalStyle(false)
|
||||
|
||||
func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
if wrap {
|
||||
buf.Write([]byte(`{"data": `))
|
||||
}
|
||||
err := json.NewEncoder(buf).Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrap {
|
||||
buf.Write([]byte(`}`))
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func getMediaType(media io.Reader) (io.Reader, string) {
|
||||
if typer, ok := media.(ContentTyper); ok {
|
||||
return media, typer.ContentType()
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
typ := "application/octet-stream"
|
||||
buf, err := ioutil.ReadAll(io.LimitReader(media, 512))
|
||||
if err != nil {
|
||||
pw.CloseWithError(fmt.Errorf("error reading media: %v", err))
|
||||
return pr, typ
|
||||
}
|
||||
typ = http.DetectContentType(buf)
|
||||
mr := io.MultiReader(bytes.NewReader(buf), media)
|
||||
go func() {
|
||||
_, err = io.Copy(pw, mr)
|
||||
if err != nil {
|
||||
pw.CloseWithError(fmt.Errorf("error reading media: %v", err))
|
||||
return
|
||||
}
|
||||
pw.Close()
|
||||
}()
|
||||
return pr, typ
|
||||
}
|
||||
|
||||
// DetectMediaType detects and returns the content type of the provided media.
|
||||
// If the type can not be determined, "application/octet-stream" is returned.
|
||||
func DetectMediaType(media io.ReaderAt) string {
|
||||
if typer, ok := media.(ContentTyper); ok {
|
||||
return typer.ContentType()
|
||||
}
|
||||
|
||||
typ := "application/octet-stream"
|
||||
buf := make([]byte, 1024)
|
||||
n, err := media.ReadAt(buf, 0)
|
||||
buf = buf[:n]
|
||||
if err == nil || err == io.EOF {
|
||||
typ = http.DetectContentType(buf)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
type Lengther interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
// endingWithErrorReader from r until it returns an error. If the
|
||||
// final error from r is io.EOF and e is non-nil, e is used instead.
|
||||
type endingWithErrorReader struct {
|
||||
r io.Reader
|
||||
e error
|
||||
}
|
||||
|
||||
func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
|
||||
n, err = er.r.Read(p)
|
||||
if err == io.EOF && er.e != nil {
|
||||
err = er.e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func typeHeader(contentType string) textproto.MIMEHeader {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Type", contentType)
|
||||
return h
|
||||
}
|
||||
|
||||
// countingWriter counts the number of bytes it receives to write, but
|
||||
// discards them.
|
||||
type countingWriter struct {
|
||||
n *int64
|
||||
}
|
||||
|
||||
func (w countingWriter) Write(p []byte) (int, error) {
|
||||
*w.n += int64(len(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// ConditionallyIncludeMedia does nothing if media is nil.
|
||||
//
|
||||
// bodyp is an in/out parameter. It should initially point to the
|
||||
// reader of the application/json (or whatever) payload to send in the
|
||||
// API request. It's updated to point to the multipart body reader.
|
||||
//
|
||||
// ctypep is an in/out parameter. It should initially point to the
|
||||
// content type of the bodyp, usually "application/json". It's updated
|
||||
// to the "multipart/related" content type, with random boundary.
|
||||
//
|
||||
// The return value is the content-length of the entire multpart body.
|
||||
func ConditionallyIncludeMedia(media io.Reader, bodyp *io.Reader, ctypep *string) (cancel func(), ok bool) {
|
||||
if media == nil {
|
||||
return
|
||||
}
|
||||
// Get the media type, which might return a different reader instance.
|
||||
var mediaType string
|
||||
media, mediaType = getMediaType(media)
|
||||
|
||||
body, bodyType := *bodyp, *ctypep
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
mpw := multipart.NewWriter(pw)
|
||||
*bodyp = pr
|
||||
*ctypep = "multipart/related; boundary=" + mpw.Boundary()
|
||||
go func() {
|
||||
w, err := mpw.CreatePart(typeHeader(bodyType))
|
||||
if err != nil {
|
||||
mpw.Close()
|
||||
pw.CloseWithError(fmt.Errorf("googleapi: body CreatePart failed: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(w, body)
|
||||
if err != nil {
|
||||
mpw.Close()
|
||||
pw.CloseWithError(fmt.Errorf("googleapi: body Copy failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
w, err = mpw.CreatePart(typeHeader(mediaType))
|
||||
if err != nil {
|
||||
mpw.Close()
|
||||
pw.CloseWithError(fmt.Errorf("googleapi: media CreatePart failed: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(w, media)
|
||||
if err != nil {
|
||||
mpw.Close()
|
||||
pw.CloseWithError(fmt.Errorf("googleapi: media Copy failed: %v", err))
|
||||
return
|
||||
}
|
||||
mpw.Close()
|
||||
pw.Close()
|
||||
}()
|
||||
cancel = func() { pw.CloseWithError(errAborted) }
|
||||
return cancel, true
|
||||
}
|
||||
|
||||
var errAborted = errors.New("googleapi: upload aborted")
|
||||
|
||||
// ProgressUpdater is a function that is called upon every progress update of a resumable upload.
|
||||
// This is the only part of a resumable upload (from googleapi) that is usable by the developer.
|
||||
// The remaining usable pieces of resumable uploads is exposed in each auto-generated API.
|
||||
type ProgressUpdater func(current, total int64)
|
||||
|
||||
// ResumableUpload is used by the generated APIs to provide resumable uploads.
|
||||
// It is not used by developers directly.
|
||||
type ResumableUpload struct {
|
||||
Client *http.Client
|
||||
// URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
|
||||
URI string
|
||||
UserAgent string // User-Agent for header of the request
|
||||
// Media is the object being uploaded.
|
||||
Media io.ReaderAt
|
||||
// MediaType defines the media type, e.g. "image/jpeg".
|
||||
MediaType string
|
||||
// ContentLength is the full size of the object being uploaded.
|
||||
ContentLength int64
|
||||
|
||||
mu sync.Mutex // guards progress
|
||||
progress int64 // number of bytes uploaded so far
|
||||
started bool // whether the upload has been started
|
||||
|
||||
// Callback is an optional function that will be called upon every progress update.
|
||||
Callback ProgressUpdater
|
||||
}
|
||||
|
||||
var (
|
||||
// rangeRE matches the transfer status response from the server. $1 is the last byte index uploaded.
|
||||
rangeRE = regexp.MustCompile(`^bytes=0\-(\d+)$`)
|
||||
// chunkSize is the size of the chunks created during a resumable upload and should be a power of two.
|
||||
// 1<<18 is the minimum size supported by the Google uploader, and there is no maximum.
|
||||
chunkSize int64 = 1 << 18
|
||||
)
|
||||
|
||||
// Progress returns the number of bytes uploaded at this point.
|
||||
func (rx *ResumableUpload) Progress() int64 {
|
||||
rx.mu.Lock()
|
||||
defer rx.mu.Unlock()
|
||||
return rx.progress
|
||||
}
|
||||
|
||||
func (rx *ResumableUpload) transferStatus(ctx context.Context) (int64, *http.Response, error) {
|
||||
req, _ := http.NewRequest("POST", rx.URI, nil)
|
||||
req.ContentLength = 0
|
||||
req.Header.Set("User-Agent", rx.UserAgent)
|
||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes */%v", rx.ContentLength))
|
||||
res, err := ctxhttp.Do(ctx, rx.Client, req)
|
||||
if err != nil || res.StatusCode != statusResumeIncomplete {
|
||||
return 0, res, err
|
||||
}
|
||||
var start int64
|
||||
if m := rangeRE.FindStringSubmatch(res.Header.Get("Range")); len(m) == 2 {
|
||||
start, err = strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("unable to parse range size %v", m[1])
|
||||
}
|
||||
start += 1 // Start at the next byte
|
||||
}
|
||||
return start, res, nil
|
||||
}
|
||||
|
||||
type chunk struct {
|
||||
body io.Reader
|
||||
size int64
|
||||
err error
|
||||
}
|
||||
|
||||
func (rx *ResumableUpload) transferChunks(ctx context.Context) (*http.Response, error) {
|
||||
var start int64
|
||||
var err error
|
||||
res := &http.Response{}
|
||||
if rx.started {
|
||||
start, res, err = rx.transferStatus(ctx)
|
||||
if err != nil || res.StatusCode != statusResumeIncomplete {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
rx.started = true
|
||||
|
||||
for {
|
||||
select { // Check for cancellation
|
||||
case <-ctx.Done():
|
||||
res.StatusCode = http.StatusRequestTimeout
|
||||
return res, ctx.Err()
|
||||
default:
|
||||
}
|
||||
reqSize := rx.ContentLength - start
|
||||
if reqSize > chunkSize {
|
||||
reqSize = chunkSize
|
||||
}
|
||||
r := io.NewSectionReader(rx.Media, start, reqSize)
|
||||
req, _ := http.NewRequest("POST", rx.URI, r)
|
||||
req.ContentLength = reqSize
|
||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes %v-%v/%v", start, start+reqSize-1, rx.ContentLength))
|
||||
req.Header.Set("Content-Type", rx.MediaType)
|
||||
req.Header.Set("User-Agent", rx.UserAgent)
|
||||
res, err = ctxhttp.Do(ctx, rx.Client, req)
|
||||
start += reqSize
|
||||
if err == nil && (res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK) {
|
||||
rx.mu.Lock()
|
||||
rx.progress = start // keep track of number of bytes sent so far
|
||||
rx.mu.Unlock()
|
||||
if rx.Callback != nil {
|
||||
rx.Callback(start, rx.ContentLength)
|
||||
}
|
||||
}
|
||||
if err != nil || res.StatusCode != statusResumeIncomplete {
|
||||
break
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
var sleep = time.Sleep // override in unit tests
|
||||
|
||||
// Upload starts the process of a resumable upload with a cancellable context.
|
||||
// It retries indefinitely (with a pause of uploadPause between attempts) until cancelled.
|
||||
// It is called from the auto-generated API code and is not visible to the user.
|
||||
// rx is private to the auto-generated API code.
|
||||
func (rx *ResumableUpload) Upload(ctx context.Context) (*http.Response, error) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
for {
|
||||
res, err = rx.transferChunks(ctx)
|
||||
if err != nil || res.StatusCode == http.StatusCreated || res.StatusCode == http.StatusOK {
|
||||
return res, err
|
||||
}
|
||||
select { // Check for cancellation
|
||||
case <-ctx.Done():
|
||||
res.StatusCode = http.StatusRequestTimeout
|
||||
return res, ctx.Err()
|
||||
default:
|
||||
}
|
||||
sleep(uploadPause)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func ResolveRelative(basestr, relstr string) string {
|
||||
u, _ := url.Parse(basestr)
|
||||
rel, _ := url.Parse(relstr)
|
||||
u = u.ResolveReference(rel)
|
||||
us := u.String()
|
||||
us = strings.Replace(us, "%7B", "{", -1)
|
||||
us = strings.Replace(us, "%7D", "}", -1)
|
||||
return us
|
||||
}
|
||||
|
||||
// has4860Fix is whether this Go environment contains the fix for
|
||||
// http://golang.org/issue/4860
|
||||
var has4860Fix bool
|
||||
|
||||
// init initializes has4860Fix by checking the behavior of the net/http package.
|
||||
func init() {
|
||||
r := http.Request{
|
||||
URL: &url.URL{
|
||||
Scheme: "http",
|
||||
Opaque: "//opaque",
|
||||
},
|
||||
}
|
||||
b := &bytes.Buffer{}
|
||||
r.Write(b)
|
||||
has4860Fix = bytes.HasPrefix(b.Bytes(), []byte("GET http"))
|
||||
}
|
||||
|
||||
// SetOpaque sets u.Opaque from u.Path such that HTTP requests to it
|
||||
// don't alter any hex-escaped characters in u.Path.
|
||||
func SetOpaque(u *url.URL) {
|
||||
u.Opaque = "//" + u.Host + u.Path
|
||||
if !has4860Fix {
|
||||
u.Opaque = u.Scheme + ":" + u.Opaque
|
||||
}
|
||||
}
|
||||
|
||||
// Expand subsitutes any {encoded} strings in the URL passed in using
|
||||
// the map supplied.
|
||||
//
|
||||
// This calls SetOpaque to avoid encoding of the parameters in the URL path.
|
||||
func Expand(u *url.URL, expansions map[string]string) {
|
||||
expanded, err := uritemplates.Expand(u.Path, expansions)
|
||||
if err == nil {
|
||||
u.Path = expanded
|
||||
SetOpaque(u)
|
||||
}
|
||||
}
|
||||
|
||||
// CloseBody is used to close res.Body.
|
||||
// Prior to calling Close, it also tries to Read a small amount to see an EOF.
|
||||
// Not seeing an EOF can prevent HTTP Transports from reusing connections.
|
||||
func CloseBody(res *http.Response) {
|
||||
if res == nil || res.Body == nil {
|
||||
return
|
||||
}
|
||||
// Justification for 3 byte reads: two for up to "\r\n" after
|
||||
// a JSON/XML document, and then 1 to see EOF if we haven't yet.
|
||||
// TODO(bradfitz): detect Go 1.3+ and skip these reads.
|
||||
// See https://codereview.appspot.com/58240043
|
||||
// and https://codereview.appspot.com/49570044
|
||||
buf := make([]byte, 1)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := res.Body.Read(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
}
|
||||
|
||||
// VariantType returns the type name of the given variant.
|
||||
// If the map doesn't contain the named key or the value is not a []interface{}, "" is returned.
|
||||
// This is used to support "variant" APIs that can return one of a number of different types.
|
||||
func VariantType(t map[string]interface{}) string {
|
||||
s, _ := t["type"].(string)
|
||||
return s
|
||||
}
|
||||
|
||||
// ConvertVariant uses the JSON encoder/decoder to fill in the struct 'dst' with the fields found in variant 'v'.
|
||||
// This is used to support "variant" APIs that can return one of a number of different types.
|
||||
// It reports whether the conversion was successful.
|
||||
func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(v)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return json.Unmarshal(buf.Bytes(), dst) == nil
|
||||
}
|
||||
|
||||
// A Field names a field to be retrieved with a partial response.
|
||||
// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
|
||||
//
|
||||
// Partial responses can dramatically reduce the amount of data that must be sent to your application.
|
||||
// In order to request partial responses, you can specify the full list of fields
|
||||
// that your application needs by adding the Fields option to your request.
|
||||
//
|
||||
// Field strings use camelCase with leading lower-case characters to identify fields within the response.
|
||||
//
|
||||
// For example, if your response has a "NextPageToken" and a slice of "Items" with "Id" fields,
|
||||
// you could request just those fields like this:
|
||||
//
|
||||
// svc.Events.List().Fields("nextPageToken", "items/id").Do()
|
||||
//
|
||||
// or if you were also interested in each Item's "Updated" field, you can combine them like this:
|
||||
//
|
||||
// svc.Events.List().Fields("nextPageToken", "items(id,updated)").Do()
|
||||
//
|
||||
// More information about field formatting can be found here:
|
||||
// https://developers.google.com/+/api/#fields-syntax
|
||||
//
|
||||
// Another way to find field names is through the Google API explorer:
|
||||
// https://developers.google.com/apis-explorer/#p/
|
||||
type Field string
|
||||
|
||||
// CombineFields combines fields into a single string.
|
||||
func CombineFields(s []Field) string {
|
||||
r := make([]string, len(s))
|
||||
for i, v := range s {
|
||||
r[i] = string(v)
|
||||
}
|
||||
return strings.Join(r, ",")
|
||||
}
|
599
vendor/google.golang.org/api/googleapi/googleapi_test.go
generated
vendored
Normal file
599
vendor/google.golang.org/api/googleapi/googleapi_test.go
generated
vendored
Normal file
|
@ -0,0 +1,599 @@
|
|||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type SetOpaqueTest struct {
|
||||
in *url.URL
|
||||
wantRequestURI string
|
||||
}
|
||||
|
||||
var setOpaqueTests = []SetOpaqueTest{
|
||||
// no path
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
},
|
||||
"http://www.golang.org",
|
||||
},
|
||||
// path
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
Path: "/",
|
||||
},
|
||||
"http://www.golang.org/",
|
||||
},
|
||||
// file with hex escaping
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "www.golang.org",
|
||||
Path: "/file%20one&two",
|
||||
},
|
||||
"https://www.golang.org/file%20one&two",
|
||||
},
|
||||
// query
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
Path: "/",
|
||||
RawQuery: "q=go+language",
|
||||
},
|
||||
"http://www.golang.org/?q=go+language",
|
||||
},
|
||||
// file with hex escaping in path plus query
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "www.golang.org",
|
||||
Path: "/file%20one&two",
|
||||
RawQuery: "q=go+language",
|
||||
},
|
||||
"https://www.golang.org/file%20one&two?q=go+language",
|
||||
},
|
||||
// query with hex escaping
|
||||
{
|
||||
&url.URL{
|
||||
Scheme: "http",
|
||||
Host: "www.golang.org",
|
||||
Path: "/",
|
||||
RawQuery: "q=go%20language",
|
||||
},
|
||||
"http://www.golang.org/?q=go%20language",
|
||||
},
|
||||
}
|
||||
|
||||
// prefixTmpl is a template for the expected prefix of the output of writing
|
||||
// an HTTP request.
|
||||
const prefixTmpl = "GET %v HTTP/1.1\r\nHost: %v\r\n"
|
||||
|
||||
func TestSetOpaque(t *testing.T) {
|
||||
for _, test := range setOpaqueTests {
|
||||
u := *test.in
|
||||
SetOpaque(&u)
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
r := &http.Request{URL: &u}
|
||||
if err := r.Write(w); err != nil {
|
||||
t.Errorf("write request: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := fmt.Sprintf(prefixTmpl, test.wantRequestURI, test.in.Host)
|
||||
if got := string(w.Bytes()); !strings.HasPrefix(got, prefix) {
|
||||
t.Errorf("got %q expected prefix %q", got, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ExpandTest struct {
|
||||
in string
|
||||
expansions map[string]string
|
||||
want string
|
||||
}
|
||||
|
||||
var expandTests = []ExpandTest{
|
||||
// no expansions
|
||||
{
|
||||
"http://www.golang.org/",
|
||||
map[string]string{},
|
||||
"http://www.golang.org/",
|
||||
},
|
||||
// one expansion, no escaping
|
||||
{
|
||||
"http://www.golang.org/{bucket}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red",
|
||||
},
|
||||
"http://www.golang.org/red/delete",
|
||||
},
|
||||
// one expansion, with hex escapes
|
||||
{
|
||||
"http://www.golang.org/{bucket}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red/blue",
|
||||
},
|
||||
"http://www.golang.org/red%2Fblue/delete",
|
||||
},
|
||||
// one expansion, with space
|
||||
{
|
||||
"http://www.golang.org/{bucket}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red or blue",
|
||||
},
|
||||
"http://www.golang.org/red%20or%20blue/delete",
|
||||
},
|
||||
// expansion not found
|
||||
{
|
||||
"http://www.golang.org/{object}/delete",
|
||||
map[string]string{
|
||||
"bucket": "red or blue",
|
||||
},
|
||||
"http://www.golang.org//delete",
|
||||
},
|
||||
// multiple expansions
|
||||
{
|
||||
"http://www.golang.org/{one}/{two}/{three}/get",
|
||||
map[string]string{
|
||||
"one": "ONE",
|
||||
"two": "TWO",
|
||||
"three": "THREE",
|
||||
},
|
||||
"http://www.golang.org/ONE/TWO/THREE/get",
|
||||
},
|
||||
// utf-8 characters
|
||||
{
|
||||
"http://www.golang.org/{bucket}/get",
|
||||
map[string]string{
|
||||
"bucket": "£100",
|
||||
},
|
||||
"http://www.golang.org/%C2%A3100/get",
|
||||
},
|
||||
// punctuations
|
||||
{
|
||||
"http://www.golang.org/{bucket}/get",
|
||||
map[string]string{
|
||||
"bucket": `/\@:,.`,
|
||||
},
|
||||
"http://www.golang.org/%2F%5C%40%3A%2C./get",
|
||||
},
|
||||
// mis-matched brackets
|
||||
{
|
||||
"http://www.golang.org/{bucket/get",
|
||||
map[string]string{
|
||||
"bucket": "red",
|
||||
},
|
||||
"http://www.golang.org/{bucket/get",
|
||||
},
|
||||
// "+" prefix for suppressing escape
|
||||
// See also: http://tools.ietf.org/html/rfc6570#section-3.2.3
|
||||
{
|
||||
"http://www.golang.org/{+topic}",
|
||||
map[string]string{
|
||||
"topic": "/topics/myproject/mytopic",
|
||||
},
|
||||
// The double slashes here look weird, but it's intentional
|
||||
"http://www.golang.org//topics/myproject/mytopic",
|
||||
},
|
||||
}
|
||||
|
||||
func TestExpand(t *testing.T) {
|
||||
for i, test := range expandTests {
|
||||
u := url.URL{
|
||||
Path: test.in,
|
||||
}
|
||||
Expand(&u, test.expansions)
|
||||
got := u.Path
|
||||
if got != test.want {
|
||||
t.Errorf("got %q expected %q in test %d", got, test.want, i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CheckResponseTest struct {
|
||||
in *http.Response
|
||||
bodyText string
|
||||
want error
|
||||
errText string
|
||||
}
|
||||
|
||||
var checkResponseTests = []CheckResponseTest{
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
},
|
||||
"",
|
||||
nil,
|
||||
"",
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
`{"error":{}}`,
|
||||
&Error{
|
||||
Code: http.StatusInternalServerError,
|
||||
Body: `{"error":{}}`,
|
||||
},
|
||||
`googleapi: got HTTP response code 500 with body: {"error":{}}`,
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
},
|
||||
`{"error":{"message":"Error message for StatusNotFound."}}`,
|
||||
&Error{
|
||||
Code: http.StatusNotFound,
|
||||
Message: "Error message for StatusNotFound.",
|
||||
Body: `{"error":{"message":"Error message for StatusNotFound."}}`,
|
||||
},
|
||||
"googleapi: Error 404: Error message for StatusNotFound.",
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
`{"error":"invalid_token","error_description":"Invalid Value"}`,
|
||||
&Error{
|
||||
Code: http.StatusBadRequest,
|
||||
Body: `{"error":"invalid_token","error_description":"Invalid Value"}`,
|
||||
},
|
||||
`googleapi: got HTTP response code 400 with body: {"error":"invalid_token","error_description":"Invalid Value"}`,
|
||||
},
|
||||
{
|
||||
&http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
},
|
||||
`{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
|
||||
&Error{
|
||||
Code: http.StatusBadRequest,
|
||||
Errors: []ErrorItem{
|
||||
{
|
||||
Reason: "keyInvalid",
|
||||
Message: "Bad Request",
|
||||
},
|
||||
},
|
||||
Body: `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
|
||||
Message: "Bad Request",
|
||||
},
|
||||
"googleapi: Error 400: Bad Request, keyInvalid",
|
||||
},
|
||||
}
|
||||
|
||||
func TestCheckResponse(t *testing.T) {
|
||||
for _, test := range checkResponseTests {
|
||||
res := test.in
|
||||
if test.bodyText != "" {
|
||||
res.Body = ioutil.NopCloser(strings.NewReader(test.bodyText))
|
||||
}
|
||||
g := CheckResponse(res)
|
||||
if !reflect.DeepEqual(g, test.want) {
|
||||
t.Errorf("CheckResponse: got %v, want %v", g, test.want)
|
||||
gotJson, err := json.Marshal(g)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wantJson, err := json.Marshal(test.want)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Errorf("json(got): %q\njson(want): %q", string(gotJson), string(wantJson))
|
||||
}
|
||||
if g != nil && g.Error() != test.errText {
|
||||
t.Errorf("CheckResponse: unexpected error message.\nGot: %q\nwant: %q", g, test.errText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type VariantPoint struct {
|
||||
Type string
|
||||
Coordinates []float64
|
||||
}
|
||||
|
||||
type VariantTest struct {
|
||||
in map[string]interface{}
|
||||
result bool
|
||||
want VariantPoint
|
||||
}
|
||||
|
||||
var coords = []interface{}{1.0, 2.0}
|
||||
|
||||
var variantTests = []VariantTest{
|
||||
{
|
||||
in: map[string]interface{}{
|
||||
"type": "Point",
|
||||
"coordinates": coords,
|
||||
},
|
||||
result: true,
|
||||
want: VariantPoint{
|
||||
Type: "Point",
|
||||
Coordinates: []float64{1.0, 2.0},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: map[string]interface{}{
|
||||
"type": "Point",
|
||||
"bogus": coords,
|
||||
},
|
||||
result: true,
|
||||
want: VariantPoint{
|
||||
Type: "Point",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestVariantType(t *testing.T) {
|
||||
for _, test := range variantTests {
|
||||
if g := VariantType(test.in); g != test.want.Type {
|
||||
t.Errorf("VariantType(%v): got %v, want %v", test.in, g, test.want.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertVariant(t *testing.T) {
|
||||
for _, test := range variantTests {
|
||||
g := VariantPoint{}
|
||||
r := ConvertVariant(test.in, &g)
|
||||
if r != test.result {
|
||||
t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, r, test.result)
|
||||
}
|
||||
if !reflect.DeepEqual(g, test.want) {
|
||||
t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, g, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type unexpectedReader struct{}
|
||||
|
||||
func (unexpectedReader) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("unexpected read in test.")
|
||||
}
|
||||
|
||||
var contentRangeRE = regexp.MustCompile(`^bytes (\d+)\-(\d+)/(\d+)$`)
|
||||
|
||||
func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
t.req = req
|
||||
if rng := req.Header.Get("Content-Range"); rng != "" && !strings.HasPrefix(rng, "bytes */") { // Read the data
|
||||
m := contentRangeRE.FindStringSubmatch(rng)
|
||||
if len(m) != 4 {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
start, err := strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
end, err := strconv.ParseInt(m[2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
totalSize, err := strconv.ParseInt(m[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
partialSize := end - start + 1
|
||||
t.buf, err = ioutil.ReadAll(req.Body)
|
||||
if err != nil || int64(len(t.buf)) != partialSize {
|
||||
return nil, fmt.Errorf("unable to read %v bytes from request data, n=%v: %v", partialSize, len(t.buf), err)
|
||||
}
|
||||
if totalSize == end+1 {
|
||||
t.statusCode = 200 // signify completion of transfer
|
||||
}
|
||||
}
|
||||
f := ioutil.NopCloser(unexpectedReader{})
|
||||
res := &http.Response{
|
||||
Body: f,
|
||||
StatusCode: t.statusCode,
|
||||
Header: http.Header{},
|
||||
}
|
||||
if t.rangeVal != "" {
|
||||
res.Header.Set("Range", t.rangeVal)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type testTransport struct {
|
||||
req *http.Request
|
||||
statusCode int
|
||||
rangeVal string
|
||||
want int64
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var statusTests = []*testTransport{
|
||||
&testTransport{statusCode: 308, want: 0},
|
||||
&testTransport{statusCode: 308, rangeVal: "bytes=0-0", want: 1},
|
||||
&testTransport{statusCode: 308, rangeVal: "bytes=0-42", want: 43},
|
||||
}
|
||||
|
||||
func TestTransferStatus(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
for _, tr := range statusTests {
|
||||
rx := &ResumableUpload{
|
||||
Client: &http.Client{Transport: tr},
|
||||
}
|
||||
g, _, err := rx.transferStatus(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if g != tr.want {
|
||||
t.Errorf("transferStatus got %v, want %v", g, tr.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *interruptedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
t.req = req
|
||||
if rng := req.Header.Get("Content-Range"); rng != "" && !strings.HasPrefix(rng, "bytes */") {
|
||||
t.interruptCount += 1
|
||||
if t.interruptCount%7 == 0 { // Respond with a "service unavailable" error
|
||||
res := &http.Response{
|
||||
StatusCode: http.StatusServiceUnavailable,
|
||||
Header: http.Header{},
|
||||
}
|
||||
t.rangeVal = fmt.Sprintf("bytes=0-%v", len(t.buf)-1) // Set the response for next time
|
||||
return res, nil
|
||||
}
|
||||
m := contentRangeRE.FindStringSubmatch(rng)
|
||||
if len(m) != 4 {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
start, err := strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
end, err := strconv.ParseInt(m[2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
totalSize, err := strconv.ParseInt(m[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse content range: %v", rng)
|
||||
}
|
||||
partialSize := end - start + 1
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil || int64(len(buf)) != partialSize {
|
||||
return nil, fmt.Errorf("unable to read %v bytes from request data, n=%v: %v", partialSize, len(buf), err)
|
||||
}
|
||||
t.buf = append(t.buf, buf...)
|
||||
if totalSize == end+1 {
|
||||
t.statusCode = 200 // signify completion of transfer
|
||||
}
|
||||
}
|
||||
f := ioutil.NopCloser(unexpectedReader{})
|
||||
res := &http.Response{
|
||||
Body: f,
|
||||
StatusCode: t.statusCode,
|
||||
Header: http.Header{},
|
||||
}
|
||||
if t.rangeVal != "" {
|
||||
res.Header.Set("Range", t.rangeVal)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type interruptedTransport struct {
|
||||
req *http.Request
|
||||
statusCode int
|
||||
rangeVal string
|
||||
interruptCount int
|
||||
buf []byte
|
||||
progressBuf string
|
||||
}
|
||||
|
||||
func (tr *interruptedTransport) ProgressUpdate(current, total int64) {
|
||||
tr.progressBuf += fmt.Sprintf("%v, %v\n", current, total)
|
||||
}
|
||||
|
||||
func TestInterruptedTransferChunks(t *testing.T) {
|
||||
f, err := os.Open("googleapi.go")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open googleapi.go: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
slurp, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to slurp file: %v", err)
|
||||
}
|
||||
st, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to stat googleapi.go: %v", err)
|
||||
}
|
||||
tr := &interruptedTransport{
|
||||
statusCode: 308,
|
||||
buf: make([]byte, 0, st.Size()),
|
||||
}
|
||||
oldChunkSize := chunkSize
|
||||
defer func() { chunkSize = oldChunkSize }()
|
||||
chunkSize = 100 // override to process small chunks for test.
|
||||
|
||||
sleep = func(time.Duration) {} // override time.Sleep
|
||||
rx := &ResumableUpload{
|
||||
Client: &http.Client{Transport: tr},
|
||||
Media: f,
|
||||
MediaType: "text/plain",
|
||||
ContentLength: st.Size(),
|
||||
Callback: tr.ProgressUpdate,
|
||||
}
|
||||
res, err := rx.Upload(context.Background())
|
||||
if err != nil || res == nil || res.StatusCode != http.StatusOK {
|
||||
if res == nil {
|
||||
t.Errorf("transferChunks not successful, res=nil: %v", err)
|
||||
} else {
|
||||
t.Errorf("transferChunks not successful, statusCode=%v: %v", res.StatusCode, err)
|
||||
}
|
||||
}
|
||||
if len(tr.buf) != len(slurp) || bytes.Compare(tr.buf, slurp) != 0 {
|
||||
t.Errorf("transferred file corrupted:\ngot %s\nwant %s", tr.buf, slurp)
|
||||
}
|
||||
w := ""
|
||||
for i := chunkSize; i <= st.Size(); i += chunkSize {
|
||||
w += fmt.Sprintf("%v, %v\n", i, st.Size())
|
||||
}
|
||||
if st.Size()%chunkSize != 0 {
|
||||
w += fmt.Sprintf("%v, %v\n", st.Size(), st.Size())
|
||||
}
|
||||
if tr.progressBuf != w {
|
||||
t.Errorf("progress update error, got %v, want %v", tr.progressBuf, w)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelUpload(t *testing.T) {
|
||||
f, err := os.Open("googleapi.go")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open googleapi.go: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
st, err := f.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to stat googleapi.go: %v", err)
|
||||
}
|
||||
tr := &interruptedTransport{
|
||||
statusCode: 308,
|
||||
buf: make([]byte, 0, st.Size()),
|
||||
}
|
||||
oldChunkSize := chunkSize
|
||||
defer func() { chunkSize = oldChunkSize }()
|
||||
chunkSize = 100 // override to process small chunks for test.
|
||||
|
||||
sleep = func(time.Duration) {} // override time.Sleep
|
||||
rx := &ResumableUpload{
|
||||
Client: &http.Client{Transport: tr},
|
||||
Media: f,
|
||||
MediaType: "text/plain",
|
||||
ContentLength: st.Size(),
|
||||
Callback: tr.ProgressUpdate,
|
||||
}
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
cancelFunc() // stop the upload that hasn't started yet
|
||||
res, err := rx.Upload(ctx)
|
||||
if err == nil || res == nil || res.StatusCode != http.StatusRequestTimeout {
|
||||
if res == nil {
|
||||
t.Errorf("transferChunks not successful, got res=nil, err=%v, want StatusRequestTimeout", err)
|
||||
} else {
|
||||
t.Errorf("transferChunks not successful, got statusCode=%v, err=%v, want StatusRequestTimeout", res.StatusCode, err)
|
||||
}
|
||||
}
|
||||
}
|
18
vendor/google.golang.org/api/googleapi/internal/uritemplates/LICENSE
generated
vendored
Normal file
18
vendor/google.golang.org/api/googleapi/internal/uritemplates/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
Copyright (c) 2013 Joshua Tacoma
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
359
vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
generated
vendored
Normal file
359
vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
generated
vendored
Normal file
|
@ -0,0 +1,359 @@
|
|||
// Copyright 2013 Joshua Tacoma. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uritemplates is a level 4 implementation of RFC 6570 (URI
|
||||
// Template, http://tools.ietf.org/html/rfc6570).
|
||||
//
|
||||
// To use uritemplates, parse a template string and expand it with a value
|
||||
// map:
|
||||
//
|
||||
// template, _ := uritemplates.Parse("https://api.github.com/repos{/user,repo}")
|
||||
// values := make(map[string]interface{})
|
||||
// values["user"] = "jtacoma"
|
||||
// values["repo"] = "uritemplates"
|
||||
// expanded, _ := template.ExpandString(values)
|
||||
// fmt.Printf(expanded)
|
||||
//
|
||||
package uritemplates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
|
||||
reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
|
||||
validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
|
||||
hex = []byte("0123456789ABCDEF")
|
||||
)
|
||||
|
||||
func pctEncode(src []byte) []byte {
|
||||
dst := make([]byte, len(src)*3)
|
||||
for i, b := range src {
|
||||
buf := dst[i*3 : i*3+3]
|
||||
buf[0] = 0x25
|
||||
buf[1] = hex[b/16]
|
||||
buf[2] = hex[b%16]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func escape(s string, allowReserved bool) (escaped string) {
|
||||
if allowReserved {
|
||||
escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
} else {
|
||||
escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
return escaped
|
||||
}
|
||||
|
||||
// A UriTemplate is a parsed representation of a URI template.
|
||||
type UriTemplate struct {
|
||||
raw string
|
||||
parts []templatePart
|
||||
}
|
||||
|
||||
// Parse parses a URI template string into a UriTemplate object.
|
||||
func Parse(rawtemplate string) (template *UriTemplate, err error) {
|
||||
template = new(UriTemplate)
|
||||
template.raw = rawtemplate
|
||||
split := strings.Split(rawtemplate, "{")
|
||||
template.parts = make([]templatePart, len(split)*2-1)
|
||||
for i, s := range split {
|
||||
if i == 0 {
|
||||
if strings.Contains(s, "}") {
|
||||
err = errors.New("unexpected }")
|
||||
break
|
||||
}
|
||||
template.parts[i].raw = s
|
||||
} else {
|
||||
subsplit := strings.Split(s, "}")
|
||||
if len(subsplit) != 2 {
|
||||
err = errors.New("malformed template")
|
||||
break
|
||||
}
|
||||
expression := subsplit[0]
|
||||
template.parts[i*2-1], err = parseExpression(expression)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
template.parts[i*2].raw = subsplit[1]
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
template = nil
|
||||
}
|
||||
return template, err
|
||||
}
|
||||
|
||||
type templatePart struct {
|
||||
raw string
|
||||
terms []templateTerm
|
||||
first string
|
||||
sep string
|
||||
named bool
|
||||
ifemp string
|
||||
allowReserved bool
|
||||
}
|
||||
|
||||
type templateTerm struct {
|
||||
name string
|
||||
explode bool
|
||||
truncate int
|
||||
}
|
||||
|
||||
func parseExpression(expression string) (result templatePart, err error) {
|
||||
switch expression[0] {
|
||||
case '+':
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
case '.':
|
||||
result.first = "."
|
||||
result.sep = "."
|
||||
expression = expression[1:]
|
||||
case '/':
|
||||
result.first = "/"
|
||||
result.sep = "/"
|
||||
expression = expression[1:]
|
||||
case ';':
|
||||
result.first = ";"
|
||||
result.sep = ";"
|
||||
result.named = true
|
||||
expression = expression[1:]
|
||||
case '?':
|
||||
result.first = "?"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '&':
|
||||
result.first = "&"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '#':
|
||||
result.first = "#"
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
default:
|
||||
result.sep = ","
|
||||
}
|
||||
rawterms := strings.Split(expression, ",")
|
||||
result.terms = make([]templateTerm, len(rawterms))
|
||||
for i, raw := range rawterms {
|
||||
result.terms[i], err = parseTerm(raw)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func parseTerm(term string) (result templateTerm, err error) {
|
||||
if strings.HasSuffix(term, "*") {
|
||||
result.explode = true
|
||||
term = term[:len(term)-1]
|
||||
}
|
||||
split := strings.Split(term, ":")
|
||||
if len(split) == 1 {
|
||||
result.name = term
|
||||
} else if len(split) == 2 {
|
||||
result.name = split[0]
|
||||
var parsed int64
|
||||
parsed, err = strconv.ParseInt(split[1], 10, 0)
|
||||
result.truncate = int(parsed)
|
||||
} else {
|
||||
err = errors.New("multiple colons in same term")
|
||||
}
|
||||
if !validname.MatchString(result.name) {
|
||||
err = errors.New("not a valid name: " + result.name)
|
||||
}
|
||||
if result.explode && result.truncate > 0 {
|
||||
err = errors.New("both explode and prefix modifers on same term")
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Expand expands a URI template with a set of values to produce a string.
|
||||
func (self *UriTemplate) Expand(value interface{}) (string, error) {
|
||||
values, ismap := value.(map[string]interface{})
|
||||
if !ismap {
|
||||
if m, ismap := struct2map(value); !ismap {
|
||||
return "", errors.New("expected map[string]interface{}, struct, or pointer to struct.")
|
||||
} else {
|
||||
return self.Expand(m)
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for _, p := range self.parts {
|
||||
err := p.expand(&buf, values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (self *templatePart) expand(buf *bytes.Buffer, values map[string]interface{}) error {
|
||||
if len(self.raw) > 0 {
|
||||
buf.WriteString(self.raw)
|
||||
return nil
|
||||
}
|
||||
var zeroLen = buf.Len()
|
||||
buf.WriteString(self.first)
|
||||
var firstLen = buf.Len()
|
||||
for _, term := range self.terms {
|
||||
value, exists := values[term.name]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if buf.Len() != firstLen {
|
||||
buf.WriteString(self.sep)
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
self.expandString(buf, term, v)
|
||||
case []interface{}:
|
||||
self.expandArray(buf, term, v)
|
||||
case map[string]interface{}:
|
||||
if term.truncate > 0 {
|
||||
return errors.New("cannot truncate a map expansion")
|
||||
}
|
||||
self.expandMap(buf, term, v)
|
||||
default:
|
||||
if m, ismap := struct2map(value); ismap {
|
||||
if term.truncate > 0 {
|
||||
return errors.New("cannot truncate a map expansion")
|
||||
}
|
||||
self.expandMap(buf, term, m)
|
||||
} else {
|
||||
str := fmt.Sprintf("%v", value)
|
||||
self.expandString(buf, term, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
if buf.Len() == firstLen {
|
||||
original := buf.Bytes()[:zeroLen]
|
||||
buf.Reset()
|
||||
buf.Write(original)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
|
||||
if self.named {
|
||||
buf.WriteString(name)
|
||||
if empty {
|
||||
buf.WriteString(self.ifemp)
|
||||
} else {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
self.expandName(buf, t.name, len(s) == 0)
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
|
||||
func (self *templatePart) expandArray(buf *bytes.Buffer, t templateTerm, a []interface{}) {
|
||||
if len(a) == 0 {
|
||||
return
|
||||
} else if !t.explode {
|
||||
self.expandName(buf, t.name, false)
|
||||
}
|
||||
for i, value := range a {
|
||||
if t.explode && i > 0 {
|
||||
buf.WriteString(self.sep)
|
||||
} else if i > 0 {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
if self.named && t.explode {
|
||||
self.expandName(buf, t.name, len(s) == 0)
|
||||
}
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *templatePart) expandMap(buf *bytes.Buffer, t templateTerm, m map[string]interface{}) {
|
||||
if len(m) == 0 {
|
||||
return
|
||||
}
|
||||
if !t.explode {
|
||||
self.expandName(buf, t.name, len(m) == 0)
|
||||
}
|
||||
var firstLen = buf.Len()
|
||||
for k, value := range m {
|
||||
if firstLen != buf.Len() {
|
||||
if t.explode {
|
||||
buf.WriteString(self.sep)
|
||||
} else {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if t.explode {
|
||||
buf.WriteString(escape(k, self.allowReserved))
|
||||
buf.WriteRune('=')
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
} else {
|
||||
buf.WriteString(escape(k, self.allowReserved))
|
||||
buf.WriteRune(',')
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func struct2map(v interface{}) (map[string]interface{}, bool) {
|
||||
value := reflect.ValueOf(v)
|
||||
switch value.Type().Kind() {
|
||||
case reflect.Ptr:
|
||||
return struct2map(value.Elem().Interface())
|
||||
case reflect.Struct:
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
tag := value.Type().Field(i).Tag
|
||||
var name string
|
||||
if strings.Contains(string(tag), ":") {
|
||||
name = tag.Get("uri")
|
||||
} else {
|
||||
name = strings.TrimSpace(string(tag))
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = value.Type().Field(i).Name
|
||||
}
|
||||
m[name] = value.Field(i).Interface()
|
||||
}
|
||||
return m, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
13
vendor/google.golang.org/api/googleapi/internal/uritemplates/utils.go
generated
vendored
Normal file
13
vendor/google.golang.org/api/googleapi/internal/uritemplates/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
package uritemplates
|
||||
|
||||
func Expand(path string, expansions map[string]string) (string, error) {
|
||||
template, err := Parse(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
values := make(map[string]interface{})
|
||||
for k, v := range expansions {
|
||||
values[k] = v
|
||||
}
|
||||
return template.Expand(values)
|
||||
}
|
38
vendor/google.golang.org/api/googleapi/transport/apikey.go
generated
vendored
Normal file
38
vendor/google.golang.org/api/googleapi/transport/apikey.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2012 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package transport contains HTTP transports used to make
|
||||
// authenticated API requests.
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// APIKey is an HTTP Transport which wraps an underlying transport and
|
||||
// appends an API Key "key" parameter to the URL of outgoing requests.
|
||||
type APIKey struct {
|
||||
// Key is the API Key to set on requests.
|
||||
Key string
|
||||
|
||||
// Transport is the underlying HTTP transport.
|
||||
// If nil, http.DefaultTransport is used.
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
func (t *APIKey) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt := t.Transport
|
||||
if rt == nil {
|
||||
rt = http.DefaultTransport
|
||||
if rt == nil {
|
||||
return nil, errors.New("googleapi/transport: no Transport specified or available")
|
||||
}
|
||||
}
|
||||
newReq := *req
|
||||
args := newReq.URL.Query()
|
||||
args.Set("key", t.Key)
|
||||
newReq.URL.RawQuery = args.Encode()
|
||||
return rt.RoundTrip(&newReq)
|
||||
}
|
182
vendor/google.golang.org/api/googleapi/types.go
generated
vendored
Normal file
182
vendor/google.golang.org/api/googleapi/types.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Int64s is a slice of int64s that marshal as quoted strings in JSON.
|
||||
type Int64s []int64
|
||||
|
||||
func (q *Int64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, int64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int32s is a slice of int32s that marshal as quoted strings in JSON.
|
||||
type Int32s []int32
|
||||
|
||||
func (q *Int32s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, int32(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64s is a slice of uint64s that marshal as quoted strings in JSON.
|
||||
type Uint64s []uint64
|
||||
|
||||
func (q *Uint64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, uint64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint32s is a slice of uint32s that marshal as quoted strings in JSON.
|
||||
type Uint32s []uint32
|
||||
|
||||
func (q *Uint32s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, uint32(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64s is a slice of float64s that marshal as quoted strings in JSON.
|
||||
type Float64s []float64
|
||||
|
||||
func (q *Float64s) UnmarshalJSON(raw []byte) error {
|
||||
*q = (*q)[:0]
|
||||
var ss []string
|
||||
if err := json.Unmarshal(raw, &ss); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range ss {
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*q = append(*q, float64(v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func quotedList(n int, fn func(dst []byte, i int) []byte) ([]byte, error) {
|
||||
dst := make([]byte, 0, 2+n*10) // somewhat arbitrary
|
||||
dst = append(dst, '[')
|
||||
for i := 0; i < n; i++ {
|
||||
if i > 0 {
|
||||
dst = append(dst, ',')
|
||||
}
|
||||
dst = append(dst, '"')
|
||||
dst = fn(dst, i)
|
||||
dst = append(dst, '"')
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func (s Int64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendInt(dst, s[i], 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Int32s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendInt(dst, int64(s[i]), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Uint64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendUint(dst, s[i], 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Uint32s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendUint(dst, uint64(s[i]), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func (s Float64s) MarshalJSON() ([]byte, error) {
|
||||
return quotedList(len(s), func(dst []byte, i int) []byte {
|
||||
return strconv.AppendFloat(dst, s[i], 'g', -1, 64)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routines for simplifying the creation of optional fields of basic type.
|
||||
*/
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value
|
||||
// to store v and returns a pointer to it.
|
||||
func Bool(v bool) *bool { return &v }
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int32(v int32) *int32 { return &v }
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int64(v int64) *int64 { return &v }
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Float64(v float64) *float64 { return &v }
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint32(v uint32) *uint32 { return &v }
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value
|
||||
// to store v and returns a pointer to it.
|
||||
func Uint64(v uint64) *uint64 { return &v }
|
||||
|
||||
// String is a helper routine that allocates a new string value
|
||||
// to store v and returns a pointer to it.
|
||||
func String(v string) *string { return &v }
|
44
vendor/google.golang.org/api/googleapi/types_test.go
generated
vendored
Normal file
44
vendor/google.golang.org/api/googleapi/types_test.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
type T struct {
|
||||
I32 Int32s
|
||||
I64 Int64s
|
||||
U32 Uint32s
|
||||
U64 Uint64s
|
||||
F64 Float64s
|
||||
}
|
||||
v := &T{
|
||||
I32: Int32s{-1, 2, 3},
|
||||
I64: Int64s{-1, 2, 1 << 33},
|
||||
U32: Uint32s{1, 2},
|
||||
U64: Uint64s{1, 2, 1 << 33},
|
||||
F64: Float64s{1.5, 3.33},
|
||||
}
|
||||
got, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := `{"I32":["-1","2","3"],"I64":["-1","2","8589934592"],"U32":["1","2"],"U64":["1","2","8589934592"],"F64":["1.5","3.33"]}`
|
||||
if string(got) != want {
|
||||
t.Fatalf("Marshal mismatch.\n got: %s\nwant: %s\n", got, want)
|
||||
}
|
||||
|
||||
v2 := new(T)
|
||||
if err := json.Unmarshal(got, v2); err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(v, v2) {
|
||||
t.Fatalf("Unmarshal didn't produce same results.\n got: %#v\nwant: %#v\n", v, v2)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue