commit
93419b0c18
612 changed files with 41 additions and 175907 deletions
|
@ -2,7 +2,6 @@ language: go
|
||||||
go:
|
go:
|
||||||
- "1.11"
|
- "1.11"
|
||||||
install:
|
install:
|
||||||
- go get github.com/constabulary/gb/...
|
|
||||||
- go get golang.org/x/lint/golint
|
- go get golang.org/x/lint/golint
|
||||||
- go get github.com/fzipp/gocyclo
|
- go get github.com/fzipp/gocyclo
|
||||||
script: ./hooks/pre-commit
|
script: ./hooks/pre-commit
|
||||||
|
|
|
@ -7,8 +7,7 @@ rageshake requires Go version 1.11 or later.
|
||||||
To run it, do:
|
To run it, do:
|
||||||
|
|
||||||
```
|
```
|
||||||
go get github.com/constabulary/gb/...
|
go build
|
||||||
gb build
|
|
||||||
./bin/rageshake
|
./bin/rageshake
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
16
go.mod
Normal file
16
go.mod
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module github.com/matrix-org/rageshake
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.0.0-20170406015231-675fad27ef35
|
||||||
|
github.com/golang/protobuf v0.0.0-20170331031902-2bba0603135d
|
||||||
|
github.com/google/go-genproto v0.0.0-20170404132009-411e09b969b1
|
||||||
|
github.com/google/go-github v0.0.0-20170401000335-12363ffc1001
|
||||||
|
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135
|
||||||
|
github.com/googleapis/gax-go v0.0.0-20170321005343-9af46dd5a171
|
||||||
|
github.com/pkg/errors v0.0.0-20171018195549-f15c970de5b7
|
||||||
|
golang.org/x/net v0.0.0-20170329170435-ffcf1bedda3b
|
||||||
|
golang.org/x/oauth2 v0.0.0-20170321013421-7fdf09982454
|
||||||
|
golang.org/x/text v0.0.0-20170401064109-f4b4367115ec
|
||||||
|
google.golang.org/grpc v0.0.0-20170405173540-b5071124392b
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e
|
||||||
|
)
|
17
go.sum
Normal file
17
go.sum
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
cloud.google.com/go v0.0.0-20170406015231-675fad27ef35/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/golang/protobuf v0.0.0-20170331031902-2bba0603135d/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-genproto v0.0.0-20170404132009-411e09b969b1/go.mod h1:3Rcd9jSoLVkV/osPrt5CogLvLiarfI8U9/x78NwhuDU=
|
||||||
|
github.com/google/go-github v0.0.0-20170401000335-12363ffc1001 h1:OK4gfzCBCtPg14E4sYsczwFhjVu1jQJZI+OEOpiTigw=
|
||||||
|
github.com/google/go-github v0.0.0-20170401000335-12363ffc1001/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
|
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
|
||||||
|
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/googleapis/gax-go v0.0.0-20170321005343-9af46dd5a171/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
|
github.com/pkg/errors v0.0.0-20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
golang.org/x/net v0.0.0-20170329170435-ffcf1bedda3b h1:Co3zyosPfwWowmu8+roHGC+aDgizpCPH3ukhubZ0Ttg=
|
||||||
|
golang.org/x/net v0.0.0-20170329170435-ffcf1bedda3b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20170321013421-7fdf09982454 h1:qH7SPXL1bLgpFB+ycaFjqQ2lI54cG8OGelAQGpmZSnc=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20170321013421-7fdf09982454/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/text v0.0.0-20170401064109-f4b4367115ec/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
google.golang.org/grpc v0.0.0-20170405173540-b5071124392b/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e h1:o/mfNjxpTLivuKEfxzzwrJ8PmulH2wEp7t713uMwKAA=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
|
@ -26,11 +26,11 @@ cd "$tmpdir"
|
||||||
git --git-dir="${git_dir}" checkout-index -a
|
git --git-dir="${git_dir}" checkout-index -a
|
||||||
|
|
||||||
# run our checks
|
# run our checks
|
||||||
golint src/...
|
golint
|
||||||
go fmt ./src/...
|
go fmt
|
||||||
go tool vet --shadow ./src
|
go vet --shadow
|
||||||
gocyclo -over 12 src/
|
gocyclo -over 12 .
|
||||||
gb test
|
go test
|
||||||
|
|
||||||
# we're done with go so can set GIT_DIR
|
# we're done with go so can set GIT_DIR
|
||||||
export GIT_DIR="$git_dir"
|
export GIT_DIR="$git_dir"
|
||||||
|
|
154
vendor/manifest
vendored
154
vendor/manifest
vendored
|
@ -1,154 +0,0 @@
|
||||||
{
|
|
||||||
"version": 0,
|
|
||||||
"dependencies": [
|
|
||||||
{
|
|
||||||
"importpath": "cloud.google.com/go/compute/metadata",
|
|
||||||
"repository": "https://code.googlesource.com/gocloud",
|
|
||||||
"revision": "675fad27ef35e552d3708858ff337d54b3b6f4d3",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/compute/metadata"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "cloud.google.com/go/internal",
|
|
||||||
"repository": "https://code.googlesource.com/gocloud",
|
|
||||||
"revision": "675fad27ef35e552d3708858ff337d54b3b6f4d3",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/internal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/golang/protobuf/proto",
|
|
||||||
"repository": "https://github.com/golang/protobuf",
|
|
||||||
"revision": "2bba0603135d7d7f5cb73b2125beeda19c09f4ef",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/proto"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/golang/protobuf/ptypes/any",
|
|
||||||
"repository": "https://github.com/golang/protobuf",
|
|
||||||
"revision": "2bba0603135d7d7f5cb73b2125beeda19c09f4ef",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/ptypes/any"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/google/go-genproto/googleapis/rpc/status",
|
|
||||||
"repository": "https://github.com/google/go-genproto",
|
|
||||||
"revision": "411e09b969b1170a9f0c467558eb4c4c110d9c77",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/googleapis/rpc/status"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/google/go-github/github",
|
|
||||||
"repository": "https://github.com/google/go-github",
|
|
||||||
"revision": "12363ffc10018c93407ebf303040586d55957271",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/google/go-querystring/query",
|
|
||||||
"repository": "https://github.com/google/go-querystring",
|
|
||||||
"revision": "53e6ce116135b80d037921a7fdd5138cf32d7a8a",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/googleapis/gax-go",
|
|
||||||
"repository": "https://github.com/googleapis/gax-go",
|
|
||||||
"revision": "9af46dd5a1713e8b5cd71106287eba3cefdde50b",
|
|
||||||
"branch": "master"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "github.com/pkg/errors",
|
|
||||||
"repository": "https://github.com/pkg/errors",
|
|
||||||
"revision": "f15c970de5b76fac0b59abb32d62c17cc7bed265",
|
|
||||||
"branch": "master"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/net/context",
|
|
||||||
"repository": "https://go.googlesource.com/net",
|
|
||||||
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/context"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/net/http2",
|
|
||||||
"repository": "https://go.googlesource.com/net",
|
|
||||||
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/http2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/net/idna",
|
|
||||||
"repository": "https://go.googlesource.com/net",
|
|
||||||
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/idna"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/net/internal/timeseries",
|
|
||||||
"repository": "https://go.googlesource.com/net",
|
|
||||||
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/internal/timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/net/lex/httplex",
|
|
||||||
"repository": "https://go.googlesource.com/net",
|
|
||||||
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/lex/httplex"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/net/trace",
|
|
||||||
"repository": "https://go.googlesource.com/net",
|
|
||||||
"revision": "ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/trace"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/oauth2",
|
|
||||||
"repository": "https://go.googlesource.com/oauth2",
|
|
||||||
"revision": "7fdf09982454086d5570c7db3e11f360194830ca",
|
|
||||||
"branch": "master"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/text/secure/bidirule",
|
|
||||||
"repository": "https://go.googlesource.com/text",
|
|
||||||
"revision": "f4b4367115ec2de254587813edaa901bc1c723a8",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/secure/bidirule"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/text/transform",
|
|
||||||
"repository": "https://go.googlesource.com/text",
|
|
||||||
"revision": "f4b4367115ec2de254587813edaa901bc1c723a8",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/transform"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/text/unicode/bidi",
|
|
||||||
"repository": "https://go.googlesource.com/text",
|
|
||||||
"revision": "f4b4367115ec2de254587813edaa901bc1c723a8",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/unicode/bidi"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "golang.org/x/text/unicode/norm",
|
|
||||||
"repository": "https://go.googlesource.com/text",
|
|
||||||
"revision": "f4b4367115ec2de254587813edaa901bc1c723a8",
|
|
||||||
"branch": "master",
|
|
||||||
"path": "/unicode/norm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "google.golang.org/grpc",
|
|
||||||
"repository": "https://github.com/grpc/grpc-go",
|
|
||||||
"revision": "b5071124392bfd416c713e6595ac149b387f7186",
|
|
||||||
"branch": "master"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"importpath": "gopkg.in/yaml.v2",
|
|
||||||
"repository": "https://gopkg.in/yaml.v2",
|
|
||||||
"revision": "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b",
|
|
||||||
"branch": "v2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,438 +0,0 @@
|
||||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package metadata provides access to Google Compute Engine (GCE)
|
|
||||||
// metadata and API service accounts.
|
|
||||||
//
|
|
||||||
// This package is a wrapper around the GCE metadata service,
|
|
||||||
// as documented at https://developers.google.com/compute/docs/metadata.
|
|
||||||
package metadata // import "cloud.google.com/go/compute/metadata"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/net/context/ctxhttp"
|
|
||||||
|
|
||||||
"cloud.google.com/go/internal"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// metadataIP is the documented metadata server IP address.
|
|
||||||
metadataIP = "169.254.169.254"
|
|
||||||
|
|
||||||
// metadataHostEnv is the environment variable specifying the
|
|
||||||
// GCE metadata hostname. If empty, the default value of
|
|
||||||
// metadataIP ("169.254.169.254") is used instead.
|
|
||||||
// This is variable name is not defined by any spec, as far as
|
|
||||||
// I know; it was made up for the Go package.
|
|
||||||
metadataHostEnv = "GCE_METADATA_HOST"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cachedValue struct {
|
|
||||||
k string
|
|
||||||
trim bool
|
|
||||||
mu sync.Mutex
|
|
||||||
v string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
projID = &cachedValue{k: "project/project-id", trim: true}
|
|
||||||
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
|
|
||||||
instID = &cachedValue{k: "instance/id", trim: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
metaClient = &http.Client{
|
|
||||||
Transport: &internal.Transport{
|
|
||||||
Base: &http.Transport{
|
|
||||||
Dial: (&net.Dialer{
|
|
||||||
Timeout: 2 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).Dial,
|
|
||||||
ResponseHeaderTimeout: 2 * time.Second,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
subscribeClient = &http.Client{
|
|
||||||
Transport: &internal.Transport{
|
|
||||||
Base: &http.Transport{
|
|
||||||
Dial: (&net.Dialer{
|
|
||||||
Timeout: 2 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).Dial,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NotDefinedError is returned when requested metadata is not defined.
|
|
||||||
//
|
|
||||||
// The underlying string is the suffix after "/computeMetadata/v1/".
|
|
||||||
//
|
|
||||||
// This error is not returned if the value is defined to be the empty
|
|
||||||
// string.
|
|
||||||
type NotDefinedError string
|
|
||||||
|
|
||||||
func (suffix NotDefinedError) Error() string {
|
|
||||||
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a value from the metadata service.
|
|
||||||
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
||||||
//
|
|
||||||
// If the GCE_METADATA_HOST environment variable is not defined, a default of
|
|
||||||
// 169.254.169.254 will be used instead.
|
|
||||||
//
|
|
||||||
// If the requested metadata is not defined, the returned error will
|
|
||||||
// be of type NotDefinedError.
|
|
||||||
func Get(suffix string) (string, error) {
|
|
||||||
val, _, err := getETag(metaClient, suffix)
|
|
||||||
return val, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// getETag returns a value from the metadata service as well as the associated
|
|
||||||
// ETag using the provided client. This func is otherwise equivalent to Get.
|
|
||||||
func getETag(client *http.Client, suffix string) (value, etag string, err error) {
|
|
||||||
// Using a fixed IP makes it very difficult to spoof the metadata service in
|
|
||||||
// a container, which is an important use-case for local testing of cloud
|
|
||||||
// deployments. To enable spoofing of the metadata service, the environment
|
|
||||||
// variable GCE_METADATA_HOST is first inspected to decide where metadata
|
|
||||||
// requests shall go.
|
|
||||||
host := os.Getenv(metadataHostEnv)
|
|
||||||
if host == "" {
|
|
||||||
// Using 169.254.169.254 instead of "metadata" here because Go
|
|
||||||
// binaries built with the "netgo" tag and without cgo won't
|
|
||||||
// know the search suffix for "metadata" is
|
|
||||||
// ".google.internal", and this IP address is documented as
|
|
||||||
// being stable anyway.
|
|
||||||
host = metadataIP
|
|
||||||
}
|
|
||||||
url := "http://" + host + "/computeMetadata/v1/" + suffix
|
|
||||||
req, _ := http.NewRequest("GET", url, nil)
|
|
||||||
req.Header.Set("Metadata-Flavor", "Google")
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode == http.StatusNotFound {
|
|
||||||
return "", "", NotDefinedError(suffix)
|
|
||||||
}
|
|
||||||
if res.StatusCode != 200 {
|
|
||||||
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
|
|
||||||
}
|
|
||||||
all, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return string(all), res.Header.Get("Etag"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTrimmed(suffix string) (s string, err error) {
|
|
||||||
s, err = Get(suffix)
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cachedValue) get() (v string, err error) {
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.v != "" {
|
|
||||||
return c.v, nil
|
|
||||||
}
|
|
||||||
if c.trim {
|
|
||||||
v, err = getTrimmed(c.k)
|
|
||||||
} else {
|
|
||||||
v, err = Get(c.k)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
c.v = v
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
onGCEOnce sync.Once
|
|
||||||
onGCE bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// OnGCE reports whether this process is running on Google Compute Engine.
|
|
||||||
func OnGCE() bool {
|
|
||||||
onGCEOnce.Do(initOnGCE)
|
|
||||||
return onGCE
|
|
||||||
}
|
|
||||||
|
|
||||||
func initOnGCE() {
|
|
||||||
onGCE = testOnGCE()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testOnGCE() bool {
|
|
||||||
// The user explicitly said they're on GCE, so trust them.
|
|
||||||
if os.Getenv(metadataHostEnv) != "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
resc := make(chan bool, 2)
|
|
||||||
|
|
||||||
// Try two strategies in parallel.
|
|
||||||
// See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
|
|
||||||
go func() {
|
|
||||||
res, err := ctxhttp.Get(ctx, metaClient, "http://"+metadataIP)
|
|
||||||
if err != nil {
|
|
||||||
resc <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
resc <- res.Header.Get("Metadata-Flavor") == "Google"
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
addrs, err := net.LookupHost("metadata.google.internal")
|
|
||||||
if err != nil || len(addrs) == 0 {
|
|
||||||
resc <- false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resc <- strsContains(addrs, metadataIP)
|
|
||||||
}()
|
|
||||||
|
|
||||||
tryHarder := systemInfoSuggestsGCE()
|
|
||||||
if tryHarder {
|
|
||||||
res := <-resc
|
|
||||||
if res {
|
|
||||||
// The first strategy succeeded, so let's use it.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Wait for either the DNS or metadata server probe to
|
|
||||||
// contradict the other one and say we are running on
|
|
||||||
// GCE. Give it a lot of time to do so, since the system
|
|
||||||
// info already suggests we're running on a GCE BIOS.
|
|
||||||
timer := time.NewTimer(5 * time.Second)
|
|
||||||
defer timer.Stop()
|
|
||||||
select {
|
|
||||||
case res = <-resc:
|
|
||||||
return res
|
|
||||||
case <-timer.C:
|
|
||||||
// Too slow. Who knows what this system is.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's no hint from the system info that we're running on
|
|
||||||
// GCE, so use the first probe's result as truth, whether it's
|
|
||||||
// true or false. The goal here is to optimize for speed for
|
|
||||||
// users who are NOT running on GCE. We can't assume that
|
|
||||||
// either a DNS lookup or an HTTP request to a blackholed IP
|
|
||||||
// address is fast. Worst case this should return when the
|
|
||||||
// metaClient's Transport.ResponseHeaderTimeout or
|
|
||||||
// Transport.Dial.Timeout fires (in two seconds).
|
|
||||||
return <-resc
|
|
||||||
}
|
|
||||||
|
|
||||||
// systemInfoSuggestsGCE reports whether the local system (without
|
|
||||||
// doing network requests) suggests that we're running on GCE. If this
|
|
||||||
// returns true, testOnGCE tries a bit harder to reach its metadata
|
|
||||||
// server.
|
|
||||||
func systemInfoSuggestsGCE() bool {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
// We don't have any non-Linux clues available, at least yet.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
|
|
||||||
name := strings.TrimSpace(string(slurp))
|
|
||||||
return name == "Google" || name == "Google Compute Engine"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes to a value from the metadata service.
|
|
||||||
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
|
||||||
// The suffix may contain query parameters.
|
|
||||||
//
|
|
||||||
// Subscribe calls fn with the latest metadata value indicated by the provided
|
|
||||||
// suffix. If the metadata value is deleted, fn is called with the empty string
|
|
||||||
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
|
|
||||||
// is deleted. Subscribe returns the error value returned from the last call to
|
|
||||||
// fn, which may be nil when ok == false.
|
|
||||||
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
|
||||||
const failedSubscribeSleep = time.Second * 5
|
|
||||||
|
|
||||||
// First check to see if the metadata value exists at all.
|
|
||||||
val, lastETag, err := getETag(subscribeClient, suffix)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fn(val, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := true
|
|
||||||
if strings.ContainsRune(suffix, '?') {
|
|
||||||
suffix += "&wait_for_change=true&last_etag="
|
|
||||||
} else {
|
|
||||||
suffix += "?wait_for_change=true&last_etag="
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag))
|
|
||||||
if err != nil {
|
|
||||||
if _, deleted := err.(NotDefinedError); !deleted {
|
|
||||||
time.Sleep(failedSubscribeSleep)
|
|
||||||
continue // Retry on other errors.
|
|
||||||
}
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
lastETag = etag
|
|
||||||
|
|
||||||
if err := fn(val, ok); err != nil || !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectID returns the current instance's project ID string.
|
|
||||||
func ProjectID() (string, error) { return projID.get() }
|
|
||||||
|
|
||||||
// NumericProjectID returns the current instance's numeric project ID.
|
|
||||||
func NumericProjectID() (string, error) { return projNum.get() }
|
|
||||||
|
|
||||||
// InternalIP returns the instance's primary internal IP address.
|
|
||||||
func InternalIP() (string, error) {
|
|
||||||
return getTrimmed("instance/network-interfaces/0/ip")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExternalIP returns the instance's primary external (public) IP address.
|
|
||||||
func ExternalIP() (string, error) {
|
|
||||||
return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hostname returns the instance's hostname. This will be of the form
|
|
||||||
// "<instanceID>.c.<projID>.internal".
|
|
||||||
func Hostname() (string, error) {
|
|
||||||
return getTrimmed("instance/hostname")
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceTags returns the list of user-defined instance tags,
|
|
||||||
// assigned when initially creating a GCE instance.
|
|
||||||
func InstanceTags() ([]string, error) {
|
|
||||||
var s []string
|
|
||||||
j, err := Get("instance/tags")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceID returns the current VM's numeric instance ID.
|
|
||||||
func InstanceID() (string, error) {
|
|
||||||
return instID.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceName returns the current VM's instance ID string.
|
|
||||||
func InstanceName() (string, error) {
|
|
||||||
host, err := Hostname()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.Split(host, ".")[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zone returns the current VM's zone, such as "us-central1-b".
|
|
||||||
func Zone() (string, error) {
|
|
||||||
zone, err := getTrimmed("instance/zone")
|
|
||||||
// zone is of the form "projects/<projNum>/zones/<zoneName>".
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return zone[strings.LastIndex(zone, "/")+1:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceAttributes returns the list of user-defined attributes,
|
|
||||||
// assigned when initially creating a GCE VM instance. The value of an
|
|
||||||
// attribute can be obtained with InstanceAttributeValue.
|
|
||||||
func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
|
|
||||||
|
|
||||||
// ProjectAttributes returns the list of user-defined attributes
|
|
||||||
// applying to the project as a whole, not just this VM. The value of
|
|
||||||
// an attribute can be obtained with ProjectAttributeValue.
|
|
||||||
func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
|
|
||||||
|
|
||||||
func lines(suffix string) ([]string, error) {
|
|
||||||
j, err := Get(suffix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s := strings.Split(strings.TrimSpace(j), "\n")
|
|
||||||
for i := range s {
|
|
||||||
s[i] = strings.TrimSpace(s[i])
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstanceAttributeValue returns the value of the provided VM
|
|
||||||
// instance attribute.
|
|
||||||
//
|
|
||||||
// If the requested attribute is not defined, the returned error will
|
|
||||||
// be of type NotDefinedError.
|
|
||||||
//
|
|
||||||
// InstanceAttributeValue may return ("", nil) if the attribute was
|
|
||||||
// defined to be the empty string.
|
|
||||||
func InstanceAttributeValue(attr string) (string, error) {
|
|
||||||
return Get("instance/attributes/" + attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectAttributeValue returns the value of the provided
|
|
||||||
// project attribute.
|
|
||||||
//
|
|
||||||
// If the requested attribute is not defined, the returned error will
|
|
||||||
// be of type NotDefinedError.
|
|
||||||
//
|
|
||||||
// ProjectAttributeValue may return ("", nil) if the attribute was
|
|
||||||
// defined to be the empty string.
|
|
||||||
func ProjectAttributeValue(attr string) (string, error) {
|
|
||||||
return Get("project/attributes/" + attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scopes returns the service account scopes for the given account.
|
|
||||||
// The account may be empty or the string "default" to use the instance's
|
|
||||||
// main account.
|
|
||||||
func Scopes(serviceAccount string) ([]string, error) {
|
|
||||||
if serviceAccount == "" {
|
|
||||||
serviceAccount = "default"
|
|
||||||
}
|
|
||||||
return lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
|
||||||
}
|
|
||||||
|
|
||||||
func strsContains(ss []string, s string) bool {
|
|
||||||
for _, v := range ss {
|
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package metadata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestOnGCE_Stress(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping in -short mode")
|
|
||||||
}
|
|
||||||
var last bool
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
onGCEOnce = sync.Once{}
|
|
||||||
|
|
||||||
now := OnGCE()
|
|
||||||
if i > 0 && now != last {
|
|
||||||
t.Errorf("%d. changed from %v to %v", i, last, now)
|
|
||||||
}
|
|
||||||
last = now
|
|
||||||
}
|
|
||||||
t.Logf("OnGCE() = %v", last)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOnGCE_Force(t *testing.T) {
|
|
||||||
onGCEOnce = sync.Once{}
|
|
||||||
old := os.Getenv(metadataHostEnv)
|
|
||||||
defer os.Setenv(metadataHostEnv, old)
|
|
||||||
os.Setenv(metadataHostEnv, "127.0.0.1")
|
|
||||||
if !OnGCE() {
|
|
||||||
t.Error("OnGCE() = false; want true")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package atomiccache provides a map-based cache that supports very fast
|
|
||||||
// reads.
|
|
||||||
package atomiccache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mapType map[interface{}]interface{}
|
|
||||||
|
|
||||||
// Cache is a map-based cache that supports fast reads via use of atomics.
|
|
||||||
// Writes are slow, requiring a copy of the entire cache.
|
|
||||||
// The zero Cache is an empty cache, ready for use.
|
|
||||||
type Cache struct {
|
|
||||||
val atomic.Value // mapType
|
|
||||||
mu sync.Mutex // used only by writers
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the value of the cache at key. If there is no value,
|
|
||||||
// getter is called to provide one, and the cache is updated.
|
|
||||||
// The getter function may be called concurrently. It should be pure,
|
|
||||||
// returning the same value for every call.
|
|
||||||
func (c *Cache) Get(key interface{}, getter func() interface{}) interface{} {
|
|
||||||
mp, _ := c.val.Load().(mapType)
|
|
||||||
if v, ok := mp[key]; ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute value without lock.
|
|
||||||
// Might duplicate effort but won't hold other computations back.
|
|
||||||
newV := getter()
|
|
||||||
|
|
||||||
c.mu.Lock()
|
|
||||||
mp, _ = c.val.Load().(mapType)
|
|
||||||
newM := make(mapType, len(mp)+1)
|
|
||||||
for k, v := range mp {
|
|
||||||
newM[k] = v
|
|
||||||
}
|
|
||||||
newM[key] = newV
|
|
||||||
c.val.Store(newM)
|
|
||||||
c.mu.Unlock()
|
|
||||||
return newV
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package atomiccache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
|
||||||
var c Cache
|
|
||||||
called := false
|
|
||||||
get := func(k interface{}) interface{} {
|
|
||||||
return c.Get(k, func() interface{} {
|
|
||||||
called = true
|
|
||||||
return fmt.Sprintf("v%d", k)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
got := get(1)
|
|
||||||
if want := "v1"; got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
if !called {
|
|
||||||
t.Error("getter not called, expected a call")
|
|
||||||
}
|
|
||||||
called = false
|
|
||||||
got = get(1)
|
|
||||||
if want := "v1"; got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
if called {
|
|
||||||
t.Error("getter unexpectedly called")
|
|
||||||
}
|
|
||||||
}
|
|
64
vendor/src/cloud.google.com/go/internal/cloud.go
vendored
64
vendor/src/cloud.google.com/go/internal/cloud.go
vendored
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package internal provides support for the cloud packages.
|
|
||||||
//
|
|
||||||
// Users should not import this package directly.
|
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
const userAgent = "gcloud-golang/0.1"
|
|
||||||
|
|
||||||
// Transport is an http.RoundTripper that appends Google Cloud client's
|
|
||||||
// user-agent to the original request's user-agent header.
|
|
||||||
type Transport struct {
|
|
||||||
// TODO(bradfitz): delete internal.Transport. It's too wrappy for what it does.
|
|
||||||
// Do User-Agent some other way.
|
|
||||||
|
|
||||||
// Base is the actual http.RoundTripper
|
|
||||||
// requests will use. It must not be nil.
|
|
||||||
Base http.RoundTripper
|
|
||||||
}
|
|
||||||
|
|
||||||
// RoundTrip appends a user-agent to the existing user-agent
|
|
||||||
// header and delegates the request to the base http.RoundTripper.
|
|
||||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
req = cloneRequest(req)
|
|
||||||
ua := req.Header.Get("User-Agent")
|
|
||||||
if ua == "" {
|
|
||||||
ua = userAgent
|
|
||||||
} else {
|
|
||||||
ua = fmt.Sprintf("%s %s", ua, userAgent)
|
|
||||||
}
|
|
||||||
req.Header.Set("User-Agent", ua)
|
|
||||||
return t.Base.RoundTrip(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cloneRequest returns a clone of the provided *http.Request.
|
|
||||||
// The clone is a shallow copy of the struct and its Header map.
|
|
||||||
func cloneRequest(r *http.Request) *http.Request {
|
|
||||||
// shallow copy of the struct
|
|
||||||
r2 := new(http.Request)
|
|
||||||
*r2 = *r
|
|
||||||
// deep copy of the Header
|
|
||||||
r2.Header = make(http.Header)
|
|
||||||
for k, s := range r.Header {
|
|
||||||
r2.Header[k] = s
|
|
||||||
}
|
|
||||||
return r2
|
|
||||||
}
|
|
|
@ -1,444 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package fields provides a view of the fields of a struct that follows the Go
|
|
||||||
// rules, amended to consider tags and case insensitivity.
|
|
||||||
//
|
|
||||||
// Usage
|
|
||||||
//
|
|
||||||
// First define a function that interprets tags:
|
|
||||||
//
|
|
||||||
// func parseTag(st reflect.StructTag) (name string, keep bool, other interface{}, err error) { ... }
|
|
||||||
//
|
|
||||||
// The function's return values describe whether to ignore the field
|
|
||||||
// completely or provide an alternate name, as well as other data from the
|
|
||||||
// parse that is stored to avoid re-parsing.
|
|
||||||
//
|
|
||||||
// Then define a function to validate the type:
|
|
||||||
//
|
|
||||||
// func validate(t reflect.Type) error { ... }
|
|
||||||
//
|
|
||||||
// Then, if necessary, define a function to specify leaf types - types
|
|
||||||
// which should be considered one field and not be recursed into:
|
|
||||||
//
|
|
||||||
// func isLeafType(t reflect.Type) bool { ... }
|
|
||||||
//
|
|
||||||
// eg:
|
|
||||||
//
|
|
||||||
// func isLeafType(t reflect.Type) bool {
|
|
||||||
// return t == reflect.TypeOf(time.Time{})
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Next, construct a Cache, passing your functions. As its name suggests, a
|
|
||||||
// Cache remembers validation and field information for a type, so subsequent
|
|
||||||
// calls with the same type are very fast.
|
|
||||||
//
|
|
||||||
// cache := fields.NewCache(parseTag, validate, isLeafType)
|
|
||||||
//
|
|
||||||
// To get the fields of a struct type as determined by the above rules, call
|
|
||||||
// the Fields method:
|
|
||||||
//
|
|
||||||
// fields, err := cache.Fields(reflect.TypeOf(MyStruct{}))
|
|
||||||
//
|
|
||||||
// The return value can be treated as a slice of Fields.
|
|
||||||
//
|
|
||||||
// Given a string, such as a key or column name obtained during unmarshalling,
|
|
||||||
// call Match on the list of fields to find a field whose name is the best
|
|
||||||
// match:
|
|
||||||
//
|
|
||||||
// field := fields.Match(name)
|
|
||||||
//
|
|
||||||
// Match looks for an exact match first, then falls back to a case-insensitive
|
|
||||||
// comparison.
|
|
||||||
package fields
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"cloud.google.com/go/internal/atomiccache"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Field records information about a struct field.
|
|
||||||
type Field struct {
|
|
||||||
Name string // effective field name
|
|
||||||
NameFromTag bool // did Name come from a tag?
|
|
||||||
Type reflect.Type // field type
|
|
||||||
Index []int // index sequence, for reflect.Value.FieldByIndex
|
|
||||||
ParsedTag interface{} // third return value of the parseTag function
|
|
||||||
|
|
||||||
nameBytes []byte
|
|
||||||
equalFold func(s, t []byte) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParseTagFunc func(reflect.StructTag) (name string, keep bool, other interface{}, err error)
|
|
||||||
|
|
||||||
type ValidateFunc func(reflect.Type) error
|
|
||||||
|
|
||||||
type LeafTypesFunc func(reflect.Type) bool
|
|
||||||
|
|
||||||
// A Cache records information about the fields of struct types.
|
|
||||||
//
|
|
||||||
// A Cache is safe for use by multiple goroutines.
|
|
||||||
type Cache struct {
|
|
||||||
parseTag ParseTagFunc
|
|
||||||
validate ValidateFunc
|
|
||||||
leafTypes LeafTypesFunc
|
|
||||||
cache atomiccache.Cache // from reflect.Type to cacheValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCache constructs a Cache.
|
|
||||||
//
|
|
||||||
// Its first argument should be a function that accepts
|
|
||||||
// a struct tag and returns four values: an alternative name for the field
|
|
||||||
// extracted from the tag, a boolean saying whether to keep the field or ignore
|
|
||||||
// it, additional data that is stored with the field information to avoid
|
|
||||||
// having to parse the tag again, and an error.
|
|
||||||
//
|
|
||||||
// Its second argument should be a function that accepts a reflect.Type and
|
|
||||||
// returns an error if the struct type is invalid in any way. For example, it
|
|
||||||
// may check that all of the struct field tags are valid, or that all fields
|
|
||||||
// are of an appropriate type.
|
|
||||||
func NewCache(parseTag ParseTagFunc, validate ValidateFunc, leafTypes LeafTypesFunc) *Cache {
|
|
||||||
if parseTag == nil {
|
|
||||||
parseTag = func(reflect.StructTag) (string, bool, interface{}, error) {
|
|
||||||
return "", true, nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if validate == nil {
|
|
||||||
validate = func(reflect.Type) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if leafTypes == nil {
|
|
||||||
leafTypes = func(reflect.Type) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Cache{
|
|
||||||
parseTag: parseTag,
|
|
||||||
validate: validate,
|
|
||||||
leafTypes: leafTypes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A fieldScan represents an item on the fieldByNameFunc scan work list.
|
|
||||||
type fieldScan struct {
|
|
||||||
typ reflect.Type
|
|
||||||
index []int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fields returns all the exported fields of t, which must be a struct type. It
|
|
||||||
// follows the standard Go rules for embedded fields, modified by the presence
|
|
||||||
// of tags. The result is sorted lexicographically by index.
|
|
||||||
//
|
|
||||||
// These rules apply in the absence of tags:
|
|
||||||
// Anonymous struct fields are treated as if their inner exported fields were
|
|
||||||
// fields in the outer struct (embedding). The result includes all fields that
|
|
||||||
// aren't shadowed by fields at higher level of embedding. If more than one
|
|
||||||
// field with the same name exists at the same level of embedding, it is
|
|
||||||
// excluded. An anonymous field that is not of struct type is treated as having
|
|
||||||
// its type as its name.
|
|
||||||
//
|
|
||||||
// Tags modify these rules as follows:
|
|
||||||
// A field's tag is used as its name.
|
|
||||||
// An anonymous struct field with a name given in its tag is treated as
|
|
||||||
// a field having that name, rather than an embedded struct (the struct's
|
|
||||||
// fields will not be returned).
|
|
||||||
// If more than one field with the same name exists at the same level of embedding,
|
|
||||||
// but exactly one of them is tagged, then the tagged field is reported and the others
|
|
||||||
// are ignored.
|
|
||||||
func (c *Cache) Fields(t reflect.Type) (List, error) {
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
panic("fields: Fields of non-struct type")
|
|
||||||
}
|
|
||||||
return c.cachedTypeFields(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A List is a list of Fields.
|
|
||||||
type List []Field
|
|
||||||
|
|
||||||
// Match returns the field in the list whose name best matches the supplied
|
|
||||||
// name, nor nil if no field does. If there is a field with the exact name, it
|
|
||||||
// is returned. Otherwise the first field (sorted by index) whose name matches
|
|
||||||
// case-insensitively is returned.
|
|
||||||
func (l List) Match(name string) *Field {
|
|
||||||
return l.MatchBytes([]byte(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchBytes is identical to Match, except that the argument is a byte slice.
|
|
||||||
func (l List) MatchBytes(name []byte) *Field {
|
|
||||||
var f *Field
|
|
||||||
for i := range l {
|
|
||||||
ff := &l[i]
|
|
||||||
if bytes.Equal(ff.nameBytes, name) {
|
|
||||||
return ff
|
|
||||||
}
|
|
||||||
if f == nil && ff.equalFold(ff.nameBytes, name) {
|
|
||||||
f = ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
type cacheValue struct {
|
|
||||||
fields List
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
||||||
// This code has been copied and modified from
|
|
||||||
// https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/encode.go.
|
|
||||||
func (c *Cache) cachedTypeFields(t reflect.Type) (List, error) {
|
|
||||||
cv := c.cache.Get(t, func() interface{} {
|
|
||||||
if err := c.validate(t); err != nil {
|
|
||||||
return cacheValue{nil, err}
|
|
||||||
}
|
|
||||||
f, err := c.typeFields(t)
|
|
||||||
return cacheValue{List(f), err}
|
|
||||||
}).(cacheValue)
|
|
||||||
return cv.fields, cv.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) typeFields(t reflect.Type) ([]Field, error) {
|
|
||||||
fields, err := c.listFields(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Sort(byName(fields))
|
|
||||||
// Delete all fields that are hidden by the Go rules for embedded fields.
|
|
||||||
|
|
||||||
// The fields are sorted in primary order of name, secondary order of field
|
|
||||||
// index length. So the first field with a given name is the dominant one.
|
|
||||||
var out []Field
|
|
||||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
|
||||||
// One iteration per name.
|
|
||||||
// Find the sequence of fields with the name of this first field.
|
|
||||||
fi := fields[i]
|
|
||||||
name := fi.Name
|
|
||||||
for advance = 1; i+advance < len(fields); advance++ {
|
|
||||||
fj := fields[i+advance]
|
|
||||||
if fj.Name != name {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Find the dominant field, if any, out of all fields that have the same name.
|
|
||||||
dominant, ok := dominantField(fields[i : i+advance])
|
|
||||||
if ok {
|
|
||||||
out = append(out, dominant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Sort(byIndex(out))
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) listFields(t reflect.Type) ([]Field, error) {
|
|
||||||
// This uses the same condition that the Go language does: there must be a unique instance
|
|
||||||
// of the match at a given depth level. If there are multiple instances of a match at the
|
|
||||||
// same depth, they annihilate each other and inhibit any possible match at a lower level.
|
|
||||||
// The algorithm is breadth first search, one depth level at a time.
|
|
||||||
|
|
||||||
// The current and next slices are work queues:
|
|
||||||
// current lists the fields to visit on this depth level,
|
|
||||||
// and next lists the fields on the next lower level.
|
|
||||||
current := []fieldScan{}
|
|
||||||
next := []fieldScan{{typ: t}}
|
|
||||||
|
|
||||||
// nextCount records the number of times an embedded type has been
|
|
||||||
// encountered and considered for queueing in the 'next' slice.
|
|
||||||
// We only queue the first one, but we increment the count on each.
|
|
||||||
// If a struct type T can be reached more than once at a given depth level,
|
|
||||||
// then it annihilates itself and need not be considered at all when we
|
|
||||||
// process that next depth level.
|
|
||||||
var nextCount map[reflect.Type]int
|
|
||||||
|
|
||||||
// visited records the structs that have been considered already.
|
|
||||||
// Embedded pointer fields can create cycles in the graph of
|
|
||||||
// reachable embedded types; visited avoids following those cycles.
|
|
||||||
// It also avoids duplicated effort: if we didn't find the field in an
|
|
||||||
// embedded type T at level 2, we won't find it in one at level 4 either.
|
|
||||||
visited := map[reflect.Type]bool{}
|
|
||||||
|
|
||||||
var fields []Field // Fields found.
|
|
||||||
|
|
||||||
for len(next) > 0 {
|
|
||||||
current, next = next, current[:0]
|
|
||||||
count := nextCount
|
|
||||||
nextCount = nil
|
|
||||||
|
|
||||||
// Process all the fields at this depth, now listed in 'current'.
|
|
||||||
// The loop queues embedded fields found in 'next', for processing during the next
|
|
||||||
// iteration. The multiplicity of the 'current' field counts is recorded
|
|
||||||
// in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'.
|
|
||||||
for _, scan := range current {
|
|
||||||
t := scan.typ
|
|
||||||
if visited[t] {
|
|
||||||
// We've looked through this type before, at a higher level.
|
|
||||||
// That higher level would shadow the lower level we're now at,
|
|
||||||
// so this one can't be useful to us. Ignore it.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited[t] = true
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
|
|
||||||
exported := (f.PkgPath == "")
|
|
||||||
|
|
||||||
// If a named field is unexported, ignore it. An anonymous
|
|
||||||
// unexported field is processed, because it may contain
|
|
||||||
// exported fields, which are visible.
|
|
||||||
if !exported && !f.Anonymous {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Examine the tag.
|
|
||||||
tagName, keep, other, err := c.parseTag(f.Tag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !keep {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if c.leafTypes(f.Type) {
|
|
||||||
fields = append(fields, newField(f, tagName, other, scan.index, i))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var ntyp reflect.Type
|
|
||||||
if f.Anonymous {
|
|
||||||
// Anonymous field of type T or *T.
|
|
||||||
ntyp = f.Type
|
|
||||||
if ntyp.Kind() == reflect.Ptr {
|
|
||||||
ntyp = ntyp.Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record fields with a tag name, non-anonymous fields, or
|
|
||||||
// anonymous non-struct fields.
|
|
||||||
if tagName != "" || ntyp == nil || ntyp.Kind() != reflect.Struct {
|
|
||||||
if !exported {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields = append(fields, newField(f, tagName, other, scan.index, i))
|
|
||||||
if count[t] > 1 {
|
|
||||||
// If there were multiple instances, add a second,
|
|
||||||
// so that the annihilation code will see a duplicate.
|
|
||||||
fields = append(fields, fields[len(fields)-1])
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue embedded struct fields for processing with next level,
|
|
||||||
// but only if the embedded types haven't already been queued.
|
|
||||||
if nextCount[ntyp] > 0 {
|
|
||||||
nextCount[ntyp] = 2 // exact multiple doesn't matter
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if nextCount == nil {
|
|
||||||
nextCount = map[reflect.Type]int{}
|
|
||||||
}
|
|
||||||
nextCount[ntyp] = 1
|
|
||||||
if count[t] > 1 {
|
|
||||||
nextCount[ntyp] = 2 // exact multiple doesn't matter
|
|
||||||
}
|
|
||||||
var index []int
|
|
||||||
index = append(index, scan.index...)
|
|
||||||
index = append(index, i)
|
|
||||||
next = append(next, fieldScan{ntyp, index})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fields, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newField(f reflect.StructField, tagName string, other interface{}, index []int, i int) Field {
|
|
||||||
name := tagName
|
|
||||||
if name == "" {
|
|
||||||
name = f.Name
|
|
||||||
}
|
|
||||||
sf := Field{
|
|
||||||
Name: name,
|
|
||||||
NameFromTag: tagName != "",
|
|
||||||
Type: f.Type,
|
|
||||||
ParsedTag: other,
|
|
||||||
nameBytes: []byte(name),
|
|
||||||
}
|
|
||||||
sf.equalFold = foldFunc(sf.nameBytes)
|
|
||||||
sf.Index = append(sf.Index, index...)
|
|
||||||
sf.Index = append(sf.Index, i)
|
|
||||||
return sf
|
|
||||||
}
|
|
||||||
|
|
||||||
// byName sorts fields using the following criteria, in order:
|
|
||||||
// 1. name
|
|
||||||
// 2. embedding depth
|
|
||||||
// 3. tag presence (preferring a tagged field)
|
|
||||||
// 4. index sequence.
|
|
||||||
type byName []Field
|
|
||||||
|
|
||||||
func (x byName) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byName) Less(i, j int) bool {
|
|
||||||
if x[i].Name != x[j].Name {
|
|
||||||
return x[i].Name < x[j].Name
|
|
||||||
}
|
|
||||||
if len(x[i].Index) != len(x[j].Index) {
|
|
||||||
return len(x[i].Index) < len(x[j].Index)
|
|
||||||
}
|
|
||||||
if x[i].NameFromTag != x[j].NameFromTag {
|
|
||||||
return x[i].NameFromTag
|
|
||||||
}
|
|
||||||
return byIndex(x).Less(i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
// byIndex sorts field by index sequence.
|
|
||||||
type byIndex []Field
|
|
||||||
|
|
||||||
func (x byIndex) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byIndex) Less(i, j int) bool {
|
|
||||||
xi := x[i].Index
|
|
||||||
xj := x[j].Index
|
|
||||||
ln := len(xi)
|
|
||||||
if l := len(xj); l < ln {
|
|
||||||
ln = l
|
|
||||||
}
|
|
||||||
for k := 0; k < ln; k++ {
|
|
||||||
if xi[k] != xj[k] {
|
|
||||||
return xi[k] < xj[k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(xi) < len(xj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dominantField looks through the fields, all of which are known to have the
|
|
||||||
// same name, to find the single field that dominates the others using Go's
|
|
||||||
// embedding rules, modified by the presence of tags. If there are multiple
|
|
||||||
// top-level fields, the boolean will be false: This condition is an error in
|
|
||||||
// Go and we skip all the fields.
|
|
||||||
func dominantField(fs []Field) (Field, bool) {
|
|
||||||
// The fields are sorted in increasing index-length order, then by presence of tag.
|
|
||||||
// That means that the first field is the dominant one. We need only check
|
|
||||||
// for error cases: two fields at top level, either both tagged or neither tagged.
|
|
||||||
if len(fs) > 1 && len(fs[0].Index) == len(fs[1].Index) && fs[0].NameFromTag == fs[1].NameFromTag {
|
|
||||||
return Field{}, false
|
|
||||||
}
|
|
||||||
return fs[0], true
|
|
||||||
}
|
|
|
@ -1,561 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package fields
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type embed1 struct {
|
|
||||||
Em1 int
|
|
||||||
Dup int // annihilates with embed2.Dup
|
|
||||||
Shadow int
|
|
||||||
embed3
|
|
||||||
}
|
|
||||||
|
|
||||||
type embed2 struct {
|
|
||||||
Dup int
|
|
||||||
embed3
|
|
||||||
embed4
|
|
||||||
}
|
|
||||||
|
|
||||||
type embed3 struct {
|
|
||||||
Em3 int // annihilated because embed3 is in both embed1 and embed2
|
|
||||||
embed5
|
|
||||||
}
|
|
||||||
|
|
||||||
type embed4 struct {
|
|
||||||
Em4 int
|
|
||||||
Dup int // annihilation of Dup in embed1, embed2 hides this Dup
|
|
||||||
*embed1 // ignored because it occurs at a higher level
|
|
||||||
}
|
|
||||||
|
|
||||||
type embed5 struct {
|
|
||||||
x int
|
|
||||||
}
|
|
||||||
|
|
||||||
type Anonymous int
|
|
||||||
|
|
||||||
type S1 struct {
|
|
||||||
Exported int
|
|
||||||
unexported int
|
|
||||||
Shadow int // shadows S1.Shadow
|
|
||||||
embed1
|
|
||||||
*embed2
|
|
||||||
Anonymous
|
|
||||||
}
|
|
||||||
|
|
||||||
type Time struct {
|
|
||||||
time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
var intType = reflect.TypeOf(int(0))
|
|
||||||
|
|
||||||
func field(name string, tval interface{}, index ...int) *Field {
|
|
||||||
return &Field{
|
|
||||||
Name: name,
|
|
||||||
Type: reflect.TypeOf(tval),
|
|
||||||
Index: index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tfield(name string, tval interface{}, index ...int) *Field {
|
|
||||||
return &Field{
|
|
||||||
Name: name,
|
|
||||||
Type: reflect.TypeOf(tval),
|
|
||||||
Index: index,
|
|
||||||
NameFromTag: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldsNoTags(t *testing.T) {
|
|
||||||
c := NewCache(nil, nil, nil)
|
|
||||||
got, err := c.Fields(reflect.TypeOf(S1{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
want := []*Field{
|
|
||||||
field("Exported", int(0), 0),
|
|
||||||
field("Shadow", int(0), 2),
|
|
||||||
field("Em1", int(0), 3, 0),
|
|
||||||
field("Em4", int(0), 4, 2, 0),
|
|
||||||
field("Anonymous", Anonymous(0), 5),
|
|
||||||
}
|
|
||||||
if msg, ok := compareFields(got, want); !ok {
|
|
||||||
t.Error(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAgainstJSONEncodingNoTags(t *testing.T) {
|
|
||||||
// Demonstrates that this package produces the same set of fields as encoding/json.
|
|
||||||
s1 := S1{
|
|
||||||
Exported: 1,
|
|
||||||
unexported: 2,
|
|
||||||
Shadow: 3,
|
|
||||||
embed1: embed1{
|
|
||||||
Em1: 4,
|
|
||||||
Dup: 5,
|
|
||||||
Shadow: 6,
|
|
||||||
embed3: embed3{
|
|
||||||
Em3: 7,
|
|
||||||
embed5: embed5{x: 8},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
embed2: &embed2{
|
|
||||||
Dup: 9,
|
|
||||||
embed3: embed3{
|
|
||||||
Em3: 10,
|
|
||||||
embed5: embed5{x: 11},
|
|
||||||
},
|
|
||||||
embed4: embed4{
|
|
||||||
Em4: 12,
|
|
||||||
Dup: 13,
|
|
||||||
embed1: &embed1{Em1: 14},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Anonymous: Anonymous(15),
|
|
||||||
}
|
|
||||||
var want S1
|
|
||||||
jsonRoundTrip(t, s1, &want)
|
|
||||||
var got S1
|
|
||||||
got.embed2 = &embed2{} // need this because reflection won't create it
|
|
||||||
fields, err := NewCache(nil, nil, nil).Fields(reflect.TypeOf(got))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
setFields(fields, &got, s1)
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("got\n%+v\nwant\n%+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests use of LeafTypes parameter to NewCache
|
|
||||||
func TestAgainstJSONEncodingEmbeddedTime(t *testing.T) {
|
|
||||||
timeLeafFn := func(t reflect.Type) bool {
|
|
||||||
return t == reflect.TypeOf(time.Time{})
|
|
||||||
}
|
|
||||||
// Demonstrates that this package can produce the same set of
|
|
||||||
// fields as encoding/json for a struct with an embedded time.Time.
|
|
||||||
now := time.Now().UTC()
|
|
||||||
myt := Time{
|
|
||||||
now,
|
|
||||||
}
|
|
||||||
var want Time
|
|
||||||
jsonRoundTrip(t, myt, &want)
|
|
||||||
var got Time
|
|
||||||
fields, err := NewCache(nil, nil, timeLeafFn).Fields(reflect.TypeOf(got))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
setFields(fields, &got, myt)
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("got\n%+v\nwant\n%+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type S2 struct {
|
|
||||||
NoTag int
|
|
||||||
XXX int `json:"tag"` // tag name takes precedence
|
|
||||||
Anonymous `json:"anon"` // anonymous non-structs also get their name from the tag
|
|
||||||
unexported int `json:"tag"`
|
|
||||||
Embed `json:"em"` // embedded structs with tags become fields
|
|
||||||
Tag int
|
|
||||||
YYY int `json:"Tag"` // tag takes precedence over untagged field of the same name
|
|
||||||
Empty int `json:""` // empty tag is noop
|
|
||||||
tEmbed1
|
|
||||||
tEmbed2
|
|
||||||
}
|
|
||||||
|
|
||||||
type Embed struct {
|
|
||||||
Em int
|
|
||||||
}
|
|
||||||
|
|
||||||
type tEmbed1 struct {
|
|
||||||
Dup int
|
|
||||||
X int `json:"Dup2"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type tEmbed2 struct {
|
|
||||||
Y int `json:"Dup"` // takes precedence over tEmbed1.Dup because it is tagged
|
|
||||||
Z int `json:"Dup2"` // same name as tEmbed1.X and both tagged, so ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
func jsonTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) {
|
|
||||||
s := t.Get("json")
|
|
||||||
parts := strings.Split(s, ",")
|
|
||||||
if parts[0] == "-" {
|
|
||||||
return "", false, nil, nil
|
|
||||||
}
|
|
||||||
if len(parts) > 1 {
|
|
||||||
other = parts[1:]
|
|
||||||
}
|
|
||||||
return parts[0], true, other, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateFunc(t reflect.Type) (err error) {
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
return errors.New("non-struct type used")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
if t.Field(i).Type.Kind() == reflect.Slice {
|
|
||||||
return fmt.Errorf("slice field found at field %s on struct %s", t.Field(i).Name, t.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldsWithTags(t *testing.T) {
|
|
||||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
want := []*Field{
|
|
||||||
field("NoTag", int(0), 0),
|
|
||||||
tfield("tag", int(0), 1),
|
|
||||||
tfield("anon", Anonymous(0), 2),
|
|
||||||
tfield("em", Embed{}, 4),
|
|
||||||
tfield("Tag", int(0), 6),
|
|
||||||
field("Empty", int(0), 7),
|
|
||||||
tfield("Dup", int(0), 8, 0),
|
|
||||||
}
|
|
||||||
if msg, ok := compareFields(got, want); !ok {
|
|
||||||
t.Error(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAgainstJSONEncodingWithTags(t *testing.T) {
|
|
||||||
// Demonstrates that this package produces the same set of fields as encoding/json.
|
|
||||||
s2 := S2{
|
|
||||||
NoTag: 1,
|
|
||||||
XXX: 2,
|
|
||||||
Anonymous: 3,
|
|
||||||
Embed: Embed{
|
|
||||||
Em: 4,
|
|
||||||
},
|
|
||||||
tEmbed1: tEmbed1{
|
|
||||||
Dup: 5,
|
|
||||||
X: 6,
|
|
||||||
},
|
|
||||||
tEmbed2: tEmbed2{
|
|
||||||
Y: 7,
|
|
||||||
Z: 8,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
var want S2
|
|
||||||
jsonRoundTrip(t, s2, &want)
|
|
||||||
var got S2
|
|
||||||
fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(got))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
setFields(fields, &got, s2)
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("got\n%+v\nwant\n%+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnexportedAnonymousNonStruct(t *testing.T) {
|
|
||||||
// An unexported anonymous non-struct field should not be recorded.
|
|
||||||
// This is currently a bug in encoding/json.
|
|
||||||
// https://github.com/golang/go/issues/18009
|
|
||||||
type (
|
|
||||||
u int
|
|
||||||
v int
|
|
||||||
S struct {
|
|
||||||
u
|
|
||||||
v `json:"x"`
|
|
||||||
int
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(got) != 0 {
|
|
||||||
t.Errorf("got %d fields, want 0", len(got))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnexportedAnonymousStruct(t *testing.T) {
|
|
||||||
// An unexported anonymous struct with a tag is ignored.
|
|
||||||
// This is currently a bug in encoding/json.
|
|
||||||
// https://github.com/golang/go/issues/18009
|
|
||||||
type (
|
|
||||||
s1 struct{ X int }
|
|
||||||
S2 struct {
|
|
||||||
s1 `json:"Y"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S2{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(got) != 0 {
|
|
||||||
t.Errorf("got %d fields, want 0", len(got))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDominantField(t *testing.T) {
|
|
||||||
// With fields sorted by index length and then by tag presence,
|
|
||||||
// the dominant field is always the first. Make sure all error
|
|
||||||
// cases are caught.
|
|
||||||
for _, test := range []struct {
|
|
||||||
fields []Field
|
|
||||||
wantOK bool
|
|
||||||
}{
|
|
||||||
// A single field is OK.
|
|
||||||
{[]Field{{Index: []int{0}}}, true},
|
|
||||||
{[]Field{{Index: []int{0}, NameFromTag: true}}, true},
|
|
||||||
// A single field at top level is OK.
|
|
||||||
{[]Field{{Index: []int{0}}, {Index: []int{1, 0}}}, true},
|
|
||||||
{[]Field{{Index: []int{0}}, {Index: []int{1, 0}, NameFromTag: true}}, true},
|
|
||||||
{[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1, 0}, NameFromTag: true}}, true},
|
|
||||||
// A single tagged field is OK.
|
|
||||||
{[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}}}, true},
|
|
||||||
// Two untagged fields at the same level is an error.
|
|
||||||
{[]Field{{Index: []int{0}}, {Index: []int{1}}}, false},
|
|
||||||
// Two tagged fields at the same level is an error.
|
|
||||||
{[]Field{{Index: []int{0}, NameFromTag: true}, {Index: []int{1}, NameFromTag: true}}, false},
|
|
||||||
} {
|
|
||||||
_, gotOK := dominantField(test.fields)
|
|
||||||
if gotOK != test.wantOK {
|
|
||||||
t.Errorf("%v: got %t, want %t", test.fields, gotOK, test.wantOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIgnore(t *testing.T) {
|
|
||||||
type S struct {
|
|
||||||
X int `json:"-"`
|
|
||||||
}
|
|
||||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(got) != 0 {
|
|
||||||
t.Errorf("got %d fields, want 0", len(got))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsedTag(t *testing.T) {
|
|
||||||
type S struct {
|
|
||||||
X int `json:"name,omitempty"`
|
|
||||||
}
|
|
||||||
got, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
want := []*Field{
|
|
||||||
{Name: "name", NameFromTag: true, Type: intType,
|
|
||||||
Index: []int{0}, ParsedTag: []string{"omitempty"}},
|
|
||||||
}
|
|
||||||
if msg, ok := compareFields(got, want); !ok {
|
|
||||||
t.Error(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateFunc(t *testing.T) {
|
|
||||||
type MyInvalidStruct struct {
|
|
||||||
A string
|
|
||||||
B []int
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyInvalidStruct{}))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
type MyValidStruct struct {
|
|
||||||
A string
|
|
||||||
B int
|
|
||||||
}
|
|
||||||
_, err = NewCache(nil, validateFunc, nil).Fields(reflect.TypeOf(MyValidStruct{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("expected nil, got error: %s\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareFields(got []Field, want []*Field) (msg string, ok bool) {
|
|
||||||
if len(got) != len(want) {
|
|
||||||
return fmt.Sprintf("got %d fields, want %d", len(got), len(want)), false
|
|
||||||
}
|
|
||||||
for i, g := range got {
|
|
||||||
w := *want[i]
|
|
||||||
if !fieldsEqual(&g, &w) {
|
|
||||||
return fmt.Sprintf("got %+v, want %+v", g, w), false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need this because Field contains a function, which cannot be compared even
|
|
||||||
// by reflect.DeepEqual.
|
|
||||||
func fieldsEqual(f1, f2 *Field) bool {
|
|
||||||
if f1 == nil || f2 == nil {
|
|
||||||
return f1 == f2
|
|
||||||
}
|
|
||||||
return f1.Name == f2.Name &&
|
|
||||||
f1.NameFromTag == f2.NameFromTag &&
|
|
||||||
f1.Type == f2.Type &&
|
|
||||||
reflect.DeepEqual(f1.ParsedTag, f2.ParsedTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the fields of dst from those of src.
|
|
||||||
// dst must be a pointer to a struct value.
|
|
||||||
// src must be a struct value.
|
|
||||||
func setFields(fields []Field, dst, src interface{}) {
|
|
||||||
vsrc := reflect.ValueOf(src)
|
|
||||||
vdst := reflect.ValueOf(dst).Elem()
|
|
||||||
for _, f := range fields {
|
|
||||||
fdst := vdst.FieldByIndex(f.Index)
|
|
||||||
fsrc := vsrc.FieldByIndex(f.Index)
|
|
||||||
fdst.Set(fsrc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func jsonRoundTrip(t *testing.T, in, out interface{}) {
|
|
||||||
bytes, err := json.Marshal(in)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(bytes, out); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type S3 struct {
|
|
||||||
S4
|
|
||||||
Abc int
|
|
||||||
AbC int
|
|
||||||
Tag int
|
|
||||||
X int `json:"Tag"`
|
|
||||||
unexported int
|
|
||||||
}
|
|
||||||
|
|
||||||
type S4 struct {
|
|
||||||
ABc int
|
|
||||||
Y int `json:"Abc"` // ignored because of top-level Abc
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatchingField(t *testing.T) {
|
|
||||||
fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, test := range []struct {
|
|
||||||
name string
|
|
||||||
want *Field
|
|
||||||
}{
|
|
||||||
// Exact match wins.
|
|
||||||
{"Abc", field("Abc", int(0), 1)},
|
|
||||||
{"AbC", field("AbC", int(0), 2)},
|
|
||||||
{"ABc", field("ABc", int(0), 0, 0)},
|
|
||||||
// If there are multiple matches but no exact match or tag,
|
|
||||||
// the first field wins, lexicographically by index.
|
|
||||||
// Here, "ABc" is at a deeper embedding level, but since S4 appears
|
|
||||||
// first in S3, its index precedes the other fields of S3.
|
|
||||||
{"abc", field("ABc", int(0), 0, 0)},
|
|
||||||
// Tag name takes precedence over untagged field of the same name.
|
|
||||||
{"Tag", tfield("Tag", int(0), 4)},
|
|
||||||
// Unexported fields disappear.
|
|
||||||
{"unexported", nil},
|
|
||||||
// Untagged embedded structs disappear.
|
|
||||||
{"S4", nil},
|
|
||||||
} {
|
|
||||||
if got := fields.Match(test.name); !fieldsEqual(got, test.want) {
|
|
||||||
t.Errorf("match %q:\ngot %+v\nwant %+v", test.name, got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAgainstJSONMatchingField(t *testing.T) {
|
|
||||||
s3 := S3{
|
|
||||||
S4: S4{ABc: 1, Y: 2},
|
|
||||||
Abc: 3,
|
|
||||||
AbC: 4,
|
|
||||||
Tag: 5,
|
|
||||||
X: 6,
|
|
||||||
unexported: 7,
|
|
||||||
}
|
|
||||||
var want S3
|
|
||||||
jsonRoundTrip(t, s3, &want)
|
|
||||||
v := reflect.ValueOf(want)
|
|
||||||
fields, err := NewCache(jsonTagParser, nil, nil).Fields(reflect.TypeOf(S3{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, test := range []struct {
|
|
||||||
name string
|
|
||||||
got int
|
|
||||||
}{
|
|
||||||
{"Abc", 3},
|
|
||||||
{"AbC", 4},
|
|
||||||
{"ABc", 1},
|
|
||||||
{"abc", 1},
|
|
||||||
{"Tag", 6},
|
|
||||||
} {
|
|
||||||
f := fields.Match(test.name)
|
|
||||||
if f == nil {
|
|
||||||
t.Fatalf("%s: no match", test.name)
|
|
||||||
}
|
|
||||||
w := v.FieldByIndex(f.Index).Interface()
|
|
||||||
if test.got != w {
|
|
||||||
t.Errorf("%s: got %d, want %d", test.name, test.got, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTagErrors(t *testing.T) {
|
|
||||||
called := false
|
|
||||||
c := NewCache(func(t reflect.StructTag) (string, bool, interface{}, error) {
|
|
||||||
called = true
|
|
||||||
s := t.Get("f")
|
|
||||||
if s == "bad" {
|
|
||||||
return "", false, nil, errors.New("error")
|
|
||||||
}
|
|
||||||
return s, true, nil, nil
|
|
||||||
}, nil, nil)
|
|
||||||
|
|
||||||
type T struct {
|
|
||||||
X int `f:"ok"`
|
|
||||||
Y int `f:"bad"`
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := c.Fields(reflect.TypeOf(T{}))
|
|
||||||
if !called {
|
|
||||||
t.Fatal("tag parser not called")
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Error("want error, got nil")
|
|
||||||
}
|
|
||||||
// Second time, we should cache the error.
|
|
||||||
called = false
|
|
||||||
_, err = c.Fields(reflect.TypeOf(T{}))
|
|
||||||
if called {
|
|
||||||
t.Fatal("tag parser called on second time")
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Error("want error, got nil")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,156 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package fields
|
|
||||||
|
|
||||||
// This file was copied from https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/fold.go.
|
|
||||||
// Only the license and package were changed.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
|
|
||||||
kelvin = '\u212a'
|
|
||||||
smallLongEss = '\u017f'
|
|
||||||
)
|
|
||||||
|
|
||||||
// foldFunc returns one of four different case folding equivalence
|
|
||||||
// functions, from most general (and slow) to fastest:
|
|
||||||
//
|
|
||||||
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
|
|
||||||
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
|
|
||||||
// 3) asciiEqualFold, no special, but includes non-letters (including _)
|
|
||||||
// 4) simpleLetterEqualFold, no specials, no non-letters.
|
|
||||||
//
|
|
||||||
// The letters S and K are special because they map to 3 runes, not just 2:
|
|
||||||
// * S maps to s and to U+017F 'ſ' Latin small letter long s
|
|
||||||
// * k maps to K and to U+212A 'K' Kelvin sign
|
|
||||||
// See https://play.golang.org/p/tTxjOc0OGo
|
|
||||||
//
|
|
||||||
// The returned function is specialized for matching against s and
|
|
||||||
// should only be given s. It's not curried for performance reasons.
|
|
||||||
func foldFunc(s []byte) func(s, t []byte) bool {
|
|
||||||
nonLetter := false
|
|
||||||
special := false // special letter
|
|
||||||
for _, b := range s {
|
|
||||||
if b >= utf8.RuneSelf {
|
|
||||||
return bytes.EqualFold
|
|
||||||
}
|
|
||||||
upper := b & caseMask
|
|
||||||
if upper < 'A' || upper > 'Z' {
|
|
||||||
nonLetter = true
|
|
||||||
} else if upper == 'K' || upper == 'S' {
|
|
||||||
// See above for why these letters are special.
|
|
||||||
special = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if special {
|
|
||||||
return equalFoldRight
|
|
||||||
}
|
|
||||||
if nonLetter {
|
|
||||||
return asciiEqualFold
|
|
||||||
}
|
|
||||||
return simpleLetterEqualFold
|
|
||||||
}
|
|
||||||
|
|
||||||
// equalFoldRight is a specialization of bytes.EqualFold when s is
|
|
||||||
// known to be all ASCII (including punctuation), but contains an 's',
|
|
||||||
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
|
|
||||||
// See comments on foldFunc.
|
|
||||||
func equalFoldRight(s, t []byte) bool {
|
|
||||||
for _, sb := range s {
|
|
||||||
if len(t) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
tb := t[0]
|
|
||||||
if tb < utf8.RuneSelf {
|
|
||||||
if sb != tb {
|
|
||||||
sbUpper := sb & caseMask
|
|
||||||
if 'A' <= sbUpper && sbUpper <= 'Z' {
|
|
||||||
if sbUpper != tb&caseMask {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t = t[1:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// sb is ASCII and t is not. t must be either kelvin
|
|
||||||
// sign or long s; sb must be s, S, k, or K.
|
|
||||||
tr, size := utf8.DecodeRune(t)
|
|
||||||
switch sb {
|
|
||||||
case 's', 'S':
|
|
||||||
if tr != smallLongEss {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case 'k', 'K':
|
|
||||||
if tr != kelvin {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
t = t[size:]
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(t) > 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
|
||||||
// s is all ASCII (but may contain non-letters) and contains no
|
|
||||||
// special-folding letters.
|
|
||||||
// See comments on foldFunc.
|
|
||||||
func asciiEqualFold(s, t []byte) bool {
|
|
||||||
if len(s) != len(t) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, sb := range s {
|
|
||||||
tb := t[i]
|
|
||||||
if sb == tb {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') {
|
|
||||||
if sb&caseMask != tb&caseMask {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleLetterEqualFold is a specialization of bytes.EqualFold for
|
|
||||||
// use when s is all ASCII letters (no underscores, etc) and also
|
|
||||||
// doesn't contain 'k', 'K', 's', or 'S'.
|
|
||||||
// See comments on foldFunc.
|
|
||||||
func simpleLetterEqualFold(s, t []byte) bool {
|
|
||||||
if len(s) != len(t) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, b := range s {
|
|
||||||
if b&caseMask != t[i]&caseMask {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package fields
|
|
||||||
|
|
||||||
// This file was copied from https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/fold_test.go.
|
|
||||||
// Only the license and package were changed.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
var foldTests = []struct {
|
|
||||||
fn func(s, t []byte) bool
|
|
||||||
s, t string
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{equalFoldRight, "", "", true},
|
|
||||||
{equalFoldRight, "a", "a", true},
|
|
||||||
{equalFoldRight, "", "a", false},
|
|
||||||
{equalFoldRight, "a", "", false},
|
|
||||||
{equalFoldRight, "a", "A", true},
|
|
||||||
{equalFoldRight, "AB", "ab", true},
|
|
||||||
{equalFoldRight, "AB", "ac", false},
|
|
||||||
{equalFoldRight, "sbkKc", "ſbKKc", true},
|
|
||||||
{equalFoldRight, "SbKkc", "ſbKKc", true},
|
|
||||||
{equalFoldRight, "SbKkc", "ſbKK", false},
|
|
||||||
{equalFoldRight, "e", "é", false},
|
|
||||||
{equalFoldRight, "s", "S", true},
|
|
||||||
|
|
||||||
{simpleLetterEqualFold, "", "", true},
|
|
||||||
{simpleLetterEqualFold, "abc", "abc", true},
|
|
||||||
{simpleLetterEqualFold, "abc", "ABC", true},
|
|
||||||
{simpleLetterEqualFold, "abc", "ABCD", false},
|
|
||||||
{simpleLetterEqualFold, "abc", "xxx", false},
|
|
||||||
|
|
||||||
{asciiEqualFold, "a_B", "A_b", true},
|
|
||||||
{asciiEqualFold, "aa@", "aa`", false}, // verify 0x40 and 0x60 aren't case-equivalent
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFold(t *testing.T) {
|
|
||||||
for i, tt := range foldTests {
|
|
||||||
if got := tt.fn([]byte(tt.s), []byte(tt.t)); got != tt.want {
|
|
||||||
t.Errorf("%d. %q, %q = %v; want %v", i, tt.s, tt.t, got, tt.want)
|
|
||||||
}
|
|
||||||
truth := strings.EqualFold(tt.s, tt.t)
|
|
||||||
if truth != tt.want {
|
|
||||||
t.Errorf("strings.EqualFold doesn't agree with case %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFoldAgainstUnicode(t *testing.T) {
|
|
||||||
const bufSize = 5
|
|
||||||
buf1 := make([]byte, 0, bufSize)
|
|
||||||
buf2 := make([]byte, 0, bufSize)
|
|
||||||
var runes []rune
|
|
||||||
for i := 0x20; i <= 0x7f; i++ {
|
|
||||||
runes = append(runes, rune(i))
|
|
||||||
}
|
|
||||||
runes = append(runes, kelvin, smallLongEss)
|
|
||||||
|
|
||||||
funcs := []struct {
|
|
||||||
name string
|
|
||||||
fold func(s, t []byte) bool
|
|
||||||
letter bool // must be ASCII letter
|
|
||||||
simple bool // must be simple ASCII letter (not 'S' or 'K')
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "equalFoldRight",
|
|
||||||
fold: equalFoldRight,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "asciiEqualFold",
|
|
||||||
fold: asciiEqualFold,
|
|
||||||
simple: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "simpleLetterEqualFold",
|
|
||||||
fold: simpleLetterEqualFold,
|
|
||||||
simple: true,
|
|
||||||
letter: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ff := range funcs {
|
|
||||||
for _, r := range runes {
|
|
||||||
if r >= utf8.RuneSelf {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ff.letter && !isASCIILetter(byte(r)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ff.simple && (r == 's' || r == 'S' || r == 'k' || r == 'K') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, r2 := range runes {
|
|
||||||
buf1 := append(buf1[:0], 'x')
|
|
||||||
buf2 := append(buf2[:0], 'x')
|
|
||||||
buf1 = buf1[:1+utf8.EncodeRune(buf1[1:bufSize], r)]
|
|
||||||
buf2 = buf2[:1+utf8.EncodeRune(buf2[1:bufSize], r2)]
|
|
||||||
buf1 = append(buf1, 'x')
|
|
||||||
buf2 = append(buf2, 'x')
|
|
||||||
want := bytes.EqualFold(buf1, buf2)
|
|
||||||
if got := ff.fold(buf1, buf2); got != want {
|
|
||||||
t.Errorf("%s(%q, %q) = %v; want %v", ff.name, buf1, buf2, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isASCIILetter(b byte) bool {
|
|
||||||
return ('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z')
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Fail on any error
|
|
||||||
set -eo pipefail
|
|
||||||
|
|
||||||
# Display commands being run
|
|
||||||
set -x
|
|
||||||
|
|
||||||
# cd to project dir on Kokoro instance
|
|
||||||
cd git/gocloud
|
|
||||||
|
|
||||||
go version
|
|
||||||
|
|
||||||
# Set $GOPATH
|
|
||||||
export GOPATH="$HOME/go"
|
|
||||||
GOCLOUD_HOME=$GOPATH/src/cloud.google.com/go
|
|
||||||
mkdir -p $GOCLOUD_HOME
|
|
||||||
|
|
||||||
# Move code into $GOPATH and get dependencies
|
|
||||||
cp -R ./* $GOCLOUD_HOME
|
|
||||||
cd $GOCLOUD_HOME
|
|
||||||
go get -v ./...
|
|
||||||
|
|
||||||
# # Don't run integration tests until we can protect against code from
|
|
||||||
# # untrusted forks reading and storing our service account key.
|
|
||||||
# cd internal/kokoro
|
|
||||||
# # Don't print out encryption keys, etc
|
|
||||||
# set +x
|
|
||||||
# key=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_key)
|
|
||||||
# iv=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_iv)
|
|
||||||
# pass=$(cat $KOKORO_ARTIFACTS_DIR/keystore/*_encrypted_ba2d6f7723ed_pass)
|
|
||||||
|
|
||||||
# openssl aes-256-cbc -K $key -iv $iv -pass pass:$pass -in kokoro-key.json.enc -out key.json -d
|
|
||||||
# set -x
|
|
||||||
|
|
||||||
# export GCLOUD_TESTS_GOLANG_KEY="$(pwd)/key.json"
|
|
||||||
# export GCLOUD_TESTS_GOLANG_PROJECT_ID="dulcet-port-762"
|
|
||||||
# cd $GOCLOUD_HOME
|
|
||||||
|
|
||||||
# Run tests and tee output to log file, to be pushed to GCS as artifact.
|
|
||||||
go test -race -v -short ./... 2>&1 | tee $KOKORO_ARTIFACTS_DIR/$KOKORO_GERRIT_REVISION.log
|
|
||||||
|
|
||||||
# Make sure README.md is up to date.
|
|
||||||
make -C internal/readme test diff
|
|
Binary file not shown.
|
@ -1,94 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package optional provides versions of primitive types that can
|
|
||||||
// be nil. These are useful in methods that update some of an API object's
|
|
||||||
// fields.
|
|
||||||
package optional
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// Bool is either a bool or nil.
|
|
||||||
Bool interface{}
|
|
||||||
|
|
||||||
// String is either a string or nil.
|
|
||||||
String interface{}
|
|
||||||
|
|
||||||
// Int is either an int or nil.
|
|
||||||
Int interface{}
|
|
||||||
|
|
||||||
// Uint is either a uint or nil.
|
|
||||||
Uint interface{}
|
|
||||||
|
|
||||||
// Float64 is either a float64 or nil.
|
|
||||||
Float64 interface{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// ToBool returns its argument as a bool.
|
|
||||||
// It panics if its argument is nil or not a bool.
|
|
||||||
func ToBool(v Bool) bool {
|
|
||||||
x, ok := v.(bool)
|
|
||||||
if !ok {
|
|
||||||
doPanic("Bool", v)
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToString returns its argument as a string.
|
|
||||||
// It panics if its argument is nil or not a string.
|
|
||||||
func ToString(v String) string {
|
|
||||||
x, ok := v.(string)
|
|
||||||
if !ok {
|
|
||||||
doPanic("String", v)
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToInt returns its argument as an int.
|
|
||||||
// It panics if its argument is nil or not an int.
|
|
||||||
func ToInt(v Int) int {
|
|
||||||
x, ok := v.(int)
|
|
||||||
if !ok {
|
|
||||||
doPanic("Int", v)
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToUint returns its argument as a uint.
|
|
||||||
// It panics if its argument is nil or not a uint.
|
|
||||||
func ToUint(v Uint) uint {
|
|
||||||
x, ok := v.(uint)
|
|
||||||
if !ok {
|
|
||||||
doPanic("Uint", v)
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToFloat64 returns its argument as a float64.
|
|
||||||
// It panics if its argument is nil or not a float64.
|
|
||||||
func ToFloat64(v Float64) float64 {
|
|
||||||
x, ok := v.(float64)
|
|
||||||
if !ok {
|
|
||||||
doPanic("Float64", v)
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func doPanic(capType string, v interface{}) {
|
|
||||||
panic(fmt.Sprintf("optional.%s value should be %s, got %T", capType, strings.ToLower(capType), v))
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package optional
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestConvertSuccess(t *testing.T) {
|
|
||||||
if got, want := ToBool(false), false; got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := ToString(""), ""; got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := ToInt(0), 0; got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := ToUint(uint(0)), uint(0); got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
if got, want := ToFloat64(0.0), 0.0; got != want {
|
|
||||||
t.Errorf("got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertFailure(t *testing.T) {
|
|
||||||
for _, f := range []func(){
|
|
||||||
func() { ToBool(nil) },
|
|
||||||
func() { ToBool(3) },
|
|
||||||
func() { ToString(nil) },
|
|
||||||
func() { ToString(3) },
|
|
||||||
func() { ToInt(nil) },
|
|
||||||
func() { ToInt("") },
|
|
||||||
func() { ToUint(nil) },
|
|
||||||
func() { ToUint("") },
|
|
||||||
func() { ToFloat64(nil) },
|
|
||||||
func() { ToFloat64("") },
|
|
||||||
} {
|
|
||||||
if !panics(f) {
|
|
||||||
t.Error("got no panic, want panic")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func panics(f func()) (b bool) {
|
|
||||||
defer func() {
|
|
||||||
if recover() != nil {
|
|
||||||
b = true
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
f()
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Diff compares the pretty-printed representation of two values. The second
|
|
||||||
// return value reports whether the two values' representations are identical.
|
|
||||||
// If it is false, the first return value contains the diffs.
|
|
||||||
//
|
|
||||||
// The output labels the first value "want" and the second "got".
|
|
||||||
//
|
|
||||||
// Diff works by invoking the "diff" command. It will only succeed in
|
|
||||||
// environments where "diff" is on the shell path.
|
|
||||||
func Diff(want, got interface{}) (string, bool, error) {
|
|
||||||
fname1, err := writeToTemp(want)
|
|
||||||
if err != nil {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
defer os.Remove(fname1)
|
|
||||||
|
|
||||||
fname2, err := writeToTemp(got)
|
|
||||||
if err != nil {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
defer os.Remove(fname2)
|
|
||||||
|
|
||||||
cmd := exec.Command("diff", "-u", "--label=want", "--label=got", fname1, fname2)
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err == nil {
|
|
||||||
return string(out), true, nil
|
|
||||||
}
|
|
||||||
eerr, ok := err.(*exec.ExitError)
|
|
||||||
if !ok {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
ws, ok := eerr.Sys().(syscall.WaitStatus)
|
|
||||||
if !ok {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
if ws.ExitStatus() != 1 {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
// Exit status of 1 means no error, but diffs were found.
|
|
||||||
return string(out), false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeToTemp(v interface{}) (string, error) {
|
|
||||||
f, err := ioutil.TempFile("", "prettyDiff")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(f, "%+v\n", Value(v)); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package pretty
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestDiff(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
v1, v2 interface{}
|
|
||||||
ok bool
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{5, 5, true, ""},
|
|
||||||
{"foo", "foo", true, ""},
|
|
||||||
{[]int{1, 2, 3}, []int{1, 0, 3}, false, `--- want
|
|
||||||
+++ got
|
|
||||||
@@ -1,5 +1,5 @@
|
|
||||||
[]int{
|
|
||||||
1,
|
|
||||||
- 2,
|
|
||||||
+ 0,
|
|
||||||
3,
|
|
||||||
}
|
|
||||||
`},
|
|
||||||
} {
|
|
||||||
got, ok, err := Diff(test.v1, test.v2)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v vs. %v: %v", test.v1, test.v2, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ok != test.ok {
|
|
||||||
t.Errorf("%v vs. %v: got %t, want %t", test.v1, test.v2, ok, test.ok)
|
|
||||||
}
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("%v vs. %v: got:\n%q\nwant:\n%q", test.v1, test.v2, got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package pretty implements a simple pretty-printer. It is intended for
|
|
||||||
// debugging the output of tests.
|
|
||||||
//
|
|
||||||
// It follows pointers and produces multi-line output for complex values like
|
|
||||||
// slices, maps and structs.
|
|
||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Indent is the string output at each level of indentation.
|
|
||||||
var Indent = " "
|
|
||||||
|
|
||||||
// Value returns a value that will print prettily when used as an
|
|
||||||
// argument for the %v or %s format specifiers.
|
|
||||||
// With no flags, struct fields and map keys with default values are omitted.
|
|
||||||
// With the '+' or '#' flags, all values are displayed.
|
|
||||||
//
|
|
||||||
// This package does not detect cycles. Attempting to print a Value that
|
|
||||||
// contains cycles will result in unbounded recursion.
|
|
||||||
func Value(v interface{}) val { return val{v: v} }
|
|
||||||
|
|
||||||
type val struct{ v interface{} }
|
|
||||||
|
|
||||||
// Format implements the fmt.Formatter interface.
|
|
||||||
func (v val) Format(s fmt.State, c rune) {
|
|
||||||
if c == 'v' || c == 's' {
|
|
||||||
fprint(s, reflect.ValueOf(v.v), state{
|
|
||||||
defaults: s.Flag('+') || s.Flag('#'),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(s, "%%!%c(pretty.Val)", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type state struct {
|
|
||||||
level int
|
|
||||||
prefix, suffix string
|
|
||||||
defaults bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func fprint(w io.Writer, v reflect.Value, s state) {
|
|
||||||
indent := strings.Repeat(Indent, s.level)
|
|
||||||
fmt.Fprintf(w, "%s%s", indent, s.prefix)
|
|
||||||
if isNil(v) {
|
|
||||||
fmt.Fprintf(w, "nil%s", s.suffix)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v.Type().Kind() == reflect.Interface {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
for v.Type().Kind() == reflect.Ptr {
|
|
||||||
fmt.Fprintf(w, "&")
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
switch v.Type().Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(w, "%s%s", short(v), s.suffix)
|
|
||||||
|
|
||||||
case reflect.Array:
|
|
||||||
fmt.Fprintf(w, "%s{\n", v.Type())
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
fprint(w, v.Index(i), state{
|
|
||||||
level: s.level + 1,
|
|
||||||
prefix: "",
|
|
||||||
suffix: ",",
|
|
||||||
defaults: s.defaults,
|
|
||||||
})
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s}", indent)
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
fmt.Fprintf(w, "%s{", v.Type())
|
|
||||||
if v.Len() > 0 {
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
fprint(w, v.Index(i), state{
|
|
||||||
level: s.level + 1,
|
|
||||||
prefix: "",
|
|
||||||
suffix: ",",
|
|
||||||
defaults: s.defaults,
|
|
||||||
})
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s}%s", indent, s.suffix)
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
fmt.Fprintf(w, "%s{", v.Type())
|
|
||||||
if v.Len() > 0 {
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
keys := v.MapKeys()
|
|
||||||
maybeSort(keys, v.Type().Key())
|
|
||||||
for _, key := range keys {
|
|
||||||
val := v.MapIndex(key)
|
|
||||||
if s.defaults || !isDefault(val) {
|
|
||||||
fprint(w, val, state{
|
|
||||||
level: s.level + 1,
|
|
||||||
prefix: short(key) + ": ",
|
|
||||||
suffix: ",",
|
|
||||||
defaults: s.defaults,
|
|
||||||
})
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s}%s", indent, s.suffix)
|
|
||||||
|
|
||||||
case reflect.Struct:
|
|
||||||
t := v.Type()
|
|
||||||
fmt.Fprintf(w, "%s{\n", t)
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := v.Field(i)
|
|
||||||
if s.defaults || !isDefault(f) {
|
|
||||||
fprint(w, f, state{
|
|
||||||
level: s.level + 1,
|
|
||||||
prefix: t.Field(i).Name + ": ",
|
|
||||||
suffix: ",",
|
|
||||||
defaults: s.defaults,
|
|
||||||
})
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s}%s", indent, s.suffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNil(v reflect.Value) bool {
|
|
||||||
if !v.IsValid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch v.Type().Kind() {
|
|
||||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return v.IsNil()
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDefault(v reflect.Value) bool {
|
|
||||||
if !v.IsValid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
t := v.Type()
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return v.IsNil()
|
|
||||||
default:
|
|
||||||
if !v.CanInterface() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return t.Comparable() && v.Interface() == reflect.Zero(t).Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// short returns a short, one-line string for v.
|
|
||||||
func short(v reflect.Value) string {
|
|
||||||
if !v.IsValid() {
|
|
||||||
return "nil"
|
|
||||||
}
|
|
||||||
if v.Type().Kind() == reflect.String {
|
|
||||||
return fmt.Sprintf("%q", v)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%v", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func indent(w io.Writer, level int) {
|
|
||||||
for i := 0; i < level; i++ {
|
|
||||||
io.WriteString(w, Indent) // ignore errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func maybeSort(vs []reflect.Value, t reflect.Type) {
|
|
||||||
if less := lessFunc(t); less != nil {
|
|
||||||
sort.Sort(&sorter{vs, less})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lessFunc returns a function that implements the "<" operator
|
|
||||||
// for the given type, or nil if the type doesn't support "<" .
|
|
||||||
func lessFunc(t reflect.Type) func(v1, v2 interface{}) bool {
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(string) < v2.(string) }
|
|
||||||
case reflect.Int:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(int) < v2.(int) }
|
|
||||||
case reflect.Int8:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(int8) < v2.(int8) }
|
|
||||||
case reflect.Int16:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(int16) < v2.(int16) }
|
|
||||||
case reflect.Int32:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(int32) < v2.(int32) }
|
|
||||||
case reflect.Int64:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(int64) < v2.(int64) }
|
|
||||||
case reflect.Uint:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(uint) < v2.(uint) }
|
|
||||||
case reflect.Uint8:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(uint8) < v2.(uint8) }
|
|
||||||
case reflect.Uint16:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(uint16) < v2.(uint16) }
|
|
||||||
case reflect.Uint32:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(uint32) < v2.(uint32) }
|
|
||||||
case reflect.Uint64:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(uint64) < v2.(uint64) }
|
|
||||||
case reflect.Float32:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(float32) < v2.(float32) }
|
|
||||||
case reflect.Float64:
|
|
||||||
return func(v1, v2 interface{}) bool { return v1.(float64) < v2.(float64) }
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type sorter struct {
|
|
||||||
vs []reflect.Value
|
|
||||||
less func(v1, v2 interface{}) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *sorter) Len() int { return len(s.vs) }
|
|
||||||
func (s *sorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
|
|
||||||
func (s *sorter) Less(i, j int) bool { return s.less(s.vs[i].Interface(), s.vs[j].Interface()) }
|
|
|
@ -1,105 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type S struct {
|
|
||||||
X int
|
|
||||||
Y bool
|
|
||||||
z *string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSprint(t *testing.T) {
|
|
||||||
Indent = "~"
|
|
||||||
i := 17
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
|
||||||
value interface{}
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// primitives and pointer
|
|
||||||
{nil, "nil"},
|
|
||||||
{3, "3"},
|
|
||||||
{9.8, "9.8"},
|
|
||||||
{true, "true"},
|
|
||||||
{"foo", `"foo"`},
|
|
||||||
{&i, "&17"},
|
|
||||||
// array and slice
|
|
||||||
{[3]int{1, 2, 3}, "[3]int{\n~1,\n~2,\n~3,\n}"},
|
|
||||||
{[]int{1, 2, 3}, "[]int{\n~1,\n~2,\n~3,\n}"},
|
|
||||||
{[]int{}, "[]int{}"},
|
|
||||||
{[]string{"foo"}, "[]string{\n~\"foo\",\n}"},
|
|
||||||
// map
|
|
||||||
{map[int]bool{}, "map[int]bool{}"},
|
|
||||||
{map[int]bool{1: true, 2: false, 3: true},
|
|
||||||
"map[int]bool{\n~1: true,\n~3: true,\n}"},
|
|
||||||
// struct
|
|
||||||
{S{}, "pretty.S{\n}"},
|
|
||||||
{S{3, true, ptr("foo")},
|
|
||||||
"pretty.S{\n~X: 3,\n~Y: true,\n~z: &\"foo\",\n}"},
|
|
||||||
// interface
|
|
||||||
{[]interface{}{&i}, "[]interface {}{\n~&17,\n}"},
|
|
||||||
// nesting
|
|
||||||
{[]S{{1, false, ptr("a")}, {2, true, ptr("b")}},
|
|
||||||
`[]pretty.S{
|
|
||||||
~pretty.S{
|
|
||||||
~~X: 1,
|
|
||||||
~~z: &"a",
|
|
||||||
~},
|
|
||||||
~pretty.S{
|
|
||||||
~~X: 2,
|
|
||||||
~~Y: true,
|
|
||||||
~~z: &"b",
|
|
||||||
~},
|
|
||||||
}`},
|
|
||||||
} {
|
|
||||||
got := fmt.Sprintf("%v", Value(test.value))
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("%v: got:\n%q\nwant:\n%q", test.value, got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithDefaults(t *testing.T) {
|
|
||||||
Indent = "~"
|
|
||||||
for _, test := range []struct {
|
|
||||||
value interface{}
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{map[int]bool{1: true, 2: false, 3: true},
|
|
||||||
"map[int]bool{\n~1: true,\n~2: false,\n~3: true,\n}"},
|
|
||||||
{S{}, "pretty.S{\n~X: 0,\n~Y: false,\n~z: nil,\n}"},
|
|
||||||
} {
|
|
||||||
got := fmt.Sprintf("%+v", Value(test.value))
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("%v: got:\n%q\nwant:\n%q", test.value, got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBadVerb(t *testing.T) {
|
|
||||||
got := fmt.Sprintf("%d", Value(8))
|
|
||||||
want := "%!d("
|
|
||||||
if !strings.HasPrefix(got, want) {
|
|
||||||
t.Errorf("got %q, want prefix %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ptr(s string) *string { return &s }
|
|
|
@ -1,48 +0,0 @@
|
||||||
# Rebuild the README.md file at repo root by inserting code samples
|
|
||||||
# from compilable go files.
|
|
||||||
|
|
||||||
SHELL=/bin/bash
|
|
||||||
|
|
||||||
GOCLOUD_HOME=$(GOPATH)/src/cloud.google.com/go
|
|
||||||
README=$(GOCLOUD_HOME)/README.md
|
|
||||||
|
|
||||||
.PHONY: readme test test-good test-bad-go test-bad-md
|
|
||||||
|
|
||||||
readme:
|
|
||||||
@tmp=$$(mktemp); \
|
|
||||||
awk -f snipmd.awk snippets.go $(README) > $$tmp; \
|
|
||||||
mv $$tmp $(README)
|
|
||||||
|
|
||||||
diff:
|
|
||||||
diff $(README) <(awk -f snipmd.awk snippets.go $(README))
|
|
||||||
|
|
||||||
test: test-good test-bad-go test-bad-md
|
|
||||||
@echo PASS
|
|
||||||
|
|
||||||
test-good:
|
|
||||||
@echo testdata/good.md
|
|
||||||
@cd testdata >& /dev/null; \
|
|
||||||
diff -u want.md <(awk -f ../snipmd.awk snips.go good.md)
|
|
||||||
@echo "testdata/want.md (round trip)"
|
|
||||||
@cd testdata >& /dev/null; \
|
|
||||||
diff -u want.md <(awk -f ../snipmd.awk snips.go want.md)
|
|
||||||
|
|
||||||
test-bad-go:
|
|
||||||
@for f in testdata/bad-*.go; do \
|
|
||||||
echo $$f; \
|
|
||||||
if awk -f snipmd.awk $$f >& /dev/null; then \
|
|
||||||
echo "$f succeeded, want failure"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
done
|
|
||||||
|
|
||||||
test-bad-md:
|
|
||||||
@for f in testdata/bad-*.md; do \
|
|
||||||
echo $$f; \
|
|
||||||
if awk -f snipmd.awk testdata/snips.go $$f >& /dev/null; then \
|
|
||||||
echo "$f succeeded, want failure"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
# Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# snipmd inserts code snippets from Go source files into a markdown file.
|
|
||||||
#
|
|
||||||
# Call with one or more .go files and a .md file:
|
|
||||||
#
|
|
||||||
# awk -f snipmd.awk foo.go bar.go template.md
|
|
||||||
#
|
|
||||||
# In the Go files, start a snippet with
|
|
||||||
# //[ NAME
|
|
||||||
# and end it with
|
|
||||||
# //]
|
|
||||||
#
|
|
||||||
# In the markdown, write
|
|
||||||
# [snip]:# NAME
|
|
||||||
# to insert the snippet NAME just below that line.
|
|
||||||
# If there is already a code block after the [snip]:# line, it will be
|
|
||||||
# replaced, so a previous output can be used as input.
|
|
||||||
#
|
|
||||||
# The following transformations are made to the Go code:
|
|
||||||
# - The first tab of each line is removed.
|
|
||||||
# - Trailing blank lines are removed.
|
|
||||||
# - `ELLIPSIS` and `_ = ELLIPSIS` are replaced by `...`
|
|
||||||
|
|
||||||
|
|
||||||
/^[ \t]*\/\/\[/ { # start snippet in Go file
|
|
||||||
if (inGo()) {
|
|
||||||
if ($2 == "") {
|
|
||||||
die("missing snippet name")
|
|
||||||
}
|
|
||||||
curSnip = $2
|
|
||||||
next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/^[ \t]*\/\/]/ { # end snippet in Go file
|
|
||||||
if (inGo()) {
|
|
||||||
if (curSnip != "") {
|
|
||||||
# Remove all but one trailing newline.
|
|
||||||
gsub(/\n+$/, "\n", snips[curSnip])
|
|
||||||
curSnip = ""
|
|
||||||
next
|
|
||||||
} else {
|
|
||||||
die("//] without corresponding //[")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ENDFILE {
|
|
||||||
if (curSnip != "") {
|
|
||||||
die("unclosed snippet: " curSnip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Skip code blocks in the input that immediately follow [snip]:# lines,
|
|
||||||
# because we just inserted the snippet. Supports round-tripping.
|
|
||||||
/^```go$/,/^```$/ {
|
|
||||||
if (inMarkdown() && afterSnip) {
|
|
||||||
next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Matches every line.
|
|
||||||
{
|
|
||||||
if (curSnip != "") {
|
|
||||||
line = $0
|
|
||||||
# Remove initial tab, if any.
|
|
||||||
if (line ~ /^\t/) {
|
|
||||||
line = substr(line, 2)
|
|
||||||
}
|
|
||||||
# Replace ELLIPSIS.
|
|
||||||
gsub(/_ = ELLIPSIS/, "...", line)
|
|
||||||
gsub(/ELLIPSIS/, "...", line)
|
|
||||||
|
|
||||||
snips[curSnip] = snips[curSnip] line "\n"
|
|
||||||
} else if (inMarkdown()) {
|
|
||||||
afterSnip = 0
|
|
||||||
# Copy .md to output.
|
|
||||||
print
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$1 ~ /\[snip\]:#/ { # Snippet marker in .md file.
|
|
||||||
if (inMarkdown()) {
|
|
||||||
# We expect '[snip]:#' to be followed by '(NAME)'
|
|
||||||
if ($2 !~ /\(.*\)/) {
|
|
||||||
die("bad snip spec: " $0)
|
|
||||||
}
|
|
||||||
name = substr($2, 2, length($2)-2)
|
|
||||||
if (snips[name] == "") {
|
|
||||||
die("no snippet named " name)
|
|
||||||
}
|
|
||||||
printf("```go\n%s```\n", snips[name])
|
|
||||||
afterSnip = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function inMarkdown() {
|
|
||||||
return match(FILENAME, /\.md$/)
|
|
||||||
}
|
|
||||||
|
|
||||||
function inGo() {
|
|
||||||
return match(FILENAME, /\.go$/)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function die(msg) {
|
|
||||||
printf("%s:%d: %s\n", FILENAME, FNR, msg) > "/dev/stderr"
|
|
||||||
exit 1
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// This file holds samples that are embedded into README.md.
|
|
||||||
|
|
||||||
// This file has to compile, but need not execute.
|
|
||||||
// If it fails to compile, fix it, then run `make` to regenerate README.md.
|
|
||||||
|
|
||||||
package readme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"cloud.google.com/go/bigquery"
|
|
||||||
"cloud.google.com/go/datastore"
|
|
||||||
"cloud.google.com/go/logging"
|
|
||||||
"cloud.google.com/go/pubsub"
|
|
||||||
"cloud.google.com/go/spanner"
|
|
||||||
"cloud.google.com/go/storage"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"google.golang.org/api/iterator"
|
|
||||||
"google.golang.org/api/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ctx context.Context
|
|
||||||
|
|
||||||
const END = 0
|
|
||||||
|
|
||||||
func auth() {
|
|
||||||
//[ auth
|
|
||||||
client, err := storage.NewClient(ctx)
|
|
||||||
//]
|
|
||||||
_ = client
|
|
||||||
_ = err
|
|
||||||
}
|
|
||||||
|
|
||||||
func auth2() {
|
|
||||||
//[ auth-JSON
|
|
||||||
client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json"))
|
|
||||||
//]
|
|
||||||
_ = client
|
|
||||||
_ = err
|
|
||||||
}
|
|
||||||
|
|
||||||
func auth3() {
|
|
||||||
var ELLIPSIS oauth2.TokenSource
|
|
||||||
//[ auth-ts
|
|
||||||
tokenSource := ELLIPSIS
|
|
||||||
client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource))
|
|
||||||
//]
|
|
||||||
_ = client
|
|
||||||
_ = err
|
|
||||||
}
|
|
||||||
|
|
||||||
func datastoreSnippets() {
|
|
||||||
//[ datastore-1
|
|
||||||
client, err := datastore.NewClient(ctx, "my-project-id")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ datastore-2
|
|
||||||
type Post struct {
|
|
||||||
Title string
|
|
||||||
Body string `datastore:",noindex"`
|
|
||||||
PublishedAt time.Time
|
|
||||||
}
|
|
||||||
keys := []*datastore.Key{
|
|
||||||
datastore.NameKey("Post", "post1", nil),
|
|
||||||
datastore.NameKey("Post", "post2", nil),
|
|
||||||
}
|
|
||||||
posts := []*Post{
|
|
||||||
{Title: "Post 1", Body: "...", PublishedAt: time.Now()},
|
|
||||||
{Title: "Post 2", Body: "...", PublishedAt: time.Now()},
|
|
||||||
}
|
|
||||||
if _, err := client.PutMulti(ctx, keys, posts); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
func storageSnippets() {
|
|
||||||
//[ storage-1
|
|
||||||
client, err := storage.NewClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ storage-2
|
|
||||||
// Read the object1 from bucket.
|
|
||||||
rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
body, err := ioutil.ReadAll(rc)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
_ = body
|
|
||||||
}
|
|
||||||
|
|
||||||
func pubsubSnippets() {
|
|
||||||
//[ pubsub-1
|
|
||||||
client, err := pubsub.NewClient(ctx, "project-id")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
|
|
||||||
const ELLIPSIS = 0
|
|
||||||
|
|
||||||
//[ pubsub-2
|
|
||||||
// Publish "hello world" on topic1.
|
|
||||||
topic := client.Topic("topic1")
|
|
||||||
res := topic.Publish(ctx, &pubsub.Message{
|
|
||||||
Data: []byte("hello world"),
|
|
||||||
})
|
|
||||||
// The publish happens asynchronously.
|
|
||||||
// Later, you can get the result from res:
|
|
||||||
_ = ELLIPSIS
|
|
||||||
msgID, err := res.Get(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a callback to receive messages via subscription1.
|
|
||||||
sub := client.Subscription("subscription1")
|
|
||||||
err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
|
|
||||||
fmt.Println(m.Data)
|
|
||||||
m.Ack() // Acknowledge that we've consumed the message.
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
_ = msgID
|
|
||||||
}
|
|
||||||
|
|
||||||
func bqSnippets() {
|
|
||||||
//[ bq-1
|
|
||||||
c, err := bigquery.NewClient(ctx, "my-project-ID")
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ bq-2
|
|
||||||
// Construct a query.
|
|
||||||
q := c.Query(`
|
|
||||||
SELECT year, SUM(number)
|
|
||||||
FROM [bigquery-public-data:usa_names.usa_1910_2013]
|
|
||||||
WHERE name = "William"
|
|
||||||
GROUP BY year
|
|
||||||
ORDER BY year
|
|
||||||
`)
|
|
||||||
// Execute the query.
|
|
||||||
it, err := q.Read(ctx)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
// Iterate through the results.
|
|
||||||
for {
|
|
||||||
var values []bigquery.Value
|
|
||||||
err := it.Next(&values)
|
|
||||||
if err == iterator.Done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
fmt.Println(values)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
func loggingSnippets() {
|
|
||||||
//[ logging-1
|
|
||||||
ctx := context.Background()
|
|
||||||
client, err := logging.NewClient(ctx, "my-project")
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
//[ logging-2
|
|
||||||
logger := client.Logger("my-log")
|
|
||||||
logger.Log(logging.Entry{Payload: "something happened!"})
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ logging-3
|
|
||||||
err = client.Close()
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Handle error.
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
func spannerSnippets() {
|
|
||||||
//[ spanner-1
|
|
||||||
client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ spanner-2
|
|
||||||
// Simple Reads And Writes
|
|
||||||
_, err = client.Apply(ctx, []*spanner.Mutation{
|
|
||||||
spanner.Insert("Users",
|
|
||||||
[]string{"name", "email"},
|
|
||||||
[]interface{}{"alice", "a@example.com"})})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
row, err := client.Single().ReadRow(ctx, "Users",
|
|
||||||
spanner.Key{"alice"}, []string{"email"})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
_ = row
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readme
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func f() {
|
|
||||||
//[
|
|
||||||
fmt.Println()
|
|
||||||
//]
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readme
|
|
||||||
|
|
||||||
func f() {
|
|
||||||
//]
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
[snip]:# (unknown)
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
[snip]:# missing-parens
|
|
|
@ -1,21 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readme
|
|
||||||
|
|
||||||
// unclosed snippet
|
|
||||||
|
|
||||||
func f() {
|
|
||||||
//[ X
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
This template is for testing snipmd.awk.
|
|
||||||
|
|
||||||
Put the first snippet here.
|
|
||||||
|
|
||||||
[snip]:# (first)
|
|
||||||
|
|
||||||
And now the second.
|
|
||||||
[snip]:# (second)
|
|
||||||
|
|
||||||
A top-level snippet.
|
|
||||||
|
|
||||||
[snip]:# (top-level)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// A code block that is not included.
|
|
||||||
```
|
|
||||||
|
|
||||||
And we're done.
|
|
|
@ -1,39 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package readme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func f() {
|
|
||||||
ELLIPSIS := 3
|
|
||||||
//[ first
|
|
||||||
fmt.Println("hello")
|
|
||||||
x := ELLIPSIS
|
|
||||||
//]
|
|
||||||
|
|
||||||
//[ second
|
|
||||||
if x > 2 {
|
|
||||||
_ = ELLIPSIS
|
|
||||||
}
|
|
||||||
//]
|
|
||||||
}
|
|
||||||
|
|
||||||
//[ top-level
|
|
||||||
var ErrBad = errors.New("bad")
|
|
||||||
|
|
||||||
//]
|
|
|
@ -1,30 +0,0 @@
|
||||||
This template is for testing snipmd.awk.
|
|
||||||
|
|
||||||
Put the first snippet here.
|
|
||||||
|
|
||||||
[snip]:# (first)
|
|
||||||
```go
|
|
||||||
fmt.Println("hello")
|
|
||||||
x := ...
|
|
||||||
```
|
|
||||||
|
|
||||||
And now the second.
|
|
||||||
[snip]:# (second)
|
|
||||||
```go
|
|
||||||
if x > 2 {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A top-level snippet.
|
|
||||||
|
|
||||||
[snip]:# (top-level)
|
|
||||||
```go
|
|
||||||
var ErrBad = errors.New("bad")
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
// A code block that is not included.
|
|
||||||
```
|
|
||||||
|
|
||||||
And we're done.
|
|
56
vendor/src/cloud.google.com/go/internal/retry.go
vendored
56
vendor/src/cloud.google.com/go/internal/retry.go
vendored
|
@ -1,56 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
gax "github.com/googleapis/gax-go"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Retry calls the supplied function f repeatedly according to the provided
|
|
||||||
// backoff parameters. It returns when one of the following occurs:
|
|
||||||
// When f's first return value is true, Retry immediately returns with f's second
|
|
||||||
// return value.
|
|
||||||
// When the provided context is done, Retry returns with an error that
|
|
||||||
// includes both ctx.Error() and the last error returned by f.
|
|
||||||
func Retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error)) error {
|
|
||||||
return retry(ctx, bo, f, gax.Sleep)
|
|
||||||
}
|
|
||||||
|
|
||||||
func retry(ctx context.Context, bo gax.Backoff, f func() (stop bool, err error),
|
|
||||||
sleep func(context.Context, time.Duration) error) error {
|
|
||||||
var lastErr error
|
|
||||||
for {
|
|
||||||
stop, err := f()
|
|
||||||
if stop {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Remember the last "real" error from f.
|
|
||||||
if err != nil && err != context.Canceled && err != context.DeadlineExceeded {
|
|
||||||
lastErr = err
|
|
||||||
}
|
|
||||||
p := bo.Pause()
|
|
||||||
if cerr := sleep(ctx, p); cerr != nil {
|
|
||||||
if lastErr != nil {
|
|
||||||
return fmt.Errorf("%v; last function err: %v", cerr, lastErr)
|
|
||||||
}
|
|
||||||
return cerr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
gax "github.com/googleapis/gax-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRetry(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
// Without a context deadline, retry will run until the function
|
|
||||||
// says not to retry any more.
|
|
||||||
n := 0
|
|
||||||
endRetry := errors.New("end retry")
|
|
||||||
err := retry(ctx, gax.Backoff{},
|
|
||||||
func() (bool, error) {
|
|
||||||
n++
|
|
||||||
if n < 10 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return true, endRetry
|
|
||||||
},
|
|
||||||
func(context.Context, time.Duration) error { return nil })
|
|
||||||
if got, want := err, endRetry; got != want {
|
|
||||||
t.Errorf("got %v, want %v", err, endRetry)
|
|
||||||
}
|
|
||||||
if n != 10 {
|
|
||||||
t.Errorf("n: got %d, want %d", n, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the context has a deadline, sleep will return an error
|
|
||||||
// and end the function.
|
|
||||||
n = 0
|
|
||||||
err = retry(ctx, gax.Backoff{},
|
|
||||||
func() (bool, error) { return false, nil },
|
|
||||||
func(context.Context, time.Duration) error {
|
|
||||||
n++
|
|
||||||
if n < 10 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return context.DeadlineExceeded
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Error("got nil, want error")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package testutil contains helper functions for writing tests.
|
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"golang.org/x/oauth2/google"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
|
|
||||||
envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ProjID returns the project ID to use in integration tests, or the empty
|
|
||||||
// string if none is configured.
|
|
||||||
func ProjID() string {
|
|
||||||
projID := os.Getenv(envProjID)
|
|
||||||
if projID == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return projID
|
|
||||||
}
|
|
||||||
|
|
||||||
// TokenSource returns the OAuth2 token source to use in integration tests,
|
|
||||||
// or nil if none is configured. If the environment variable is unset,
|
|
||||||
// TokenSource will try to find 'Application Default Credentials'. Else,
|
|
||||||
// TokenSource will return nil.
|
|
||||||
// TokenSource will log.Fatal if the token source is specified but missing or invalid.
|
|
||||||
func TokenSource(ctx context.Context, scopes ...string) oauth2.TokenSource {
|
|
||||||
key := os.Getenv(envPrivateKey)
|
|
||||||
if key == "" { // Try for application default credentials.
|
|
||||||
ts, err := google.DefaultTokenSource(ctx, scopes...)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("No 'Application Default Credentials' found.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ts
|
|
||||||
}
|
|
||||||
jsonKey, err := ioutil.ReadFile(key)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Cannot read the JSON key file, err: %v", err)
|
|
||||||
}
|
|
||||||
conf, err := google.JWTConfigFromJSON(jsonKey, scopes...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("google.JWTConfigFromJSON: %v", err)
|
|
||||||
}
|
|
||||||
return conf.TokenSource(ctx)
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Server is an in-process gRPC server, listening on a system-chosen port on
|
|
||||||
// the local loopback interface. Servers are for testing only and are not
|
|
||||||
// intended to be used in production code.
|
|
||||||
//
|
|
||||||
// To create a server, make a new Server, register your handlers, then call
|
|
||||||
// Start:
|
|
||||||
//
|
|
||||||
// srv, err := NewServer()
|
|
||||||
// ...
|
|
||||||
// mypb.RegisterMyServiceServer(srv.Gsrv, &myHandler)
|
|
||||||
// ....
|
|
||||||
// srv.Start()
|
|
||||||
//
|
|
||||||
// Clients should connect to the server with no security:
|
|
||||||
//
|
|
||||||
// conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure())
|
|
||||||
// ...
|
|
||||||
type Server struct {
|
|
||||||
Addr string
|
|
||||||
l net.Listener
|
|
||||||
Gsrv *grpc.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer creates a new Server. The Server will be listening for gRPC connections
|
|
||||||
// at the address named by the Addr field, without TLS.
|
|
||||||
func NewServer(opts ...grpc.ServerOption) (*Server, error) {
|
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s := &Server{
|
|
||||||
Addr: l.Addr().String(),
|
|
||||||
l: l,
|
|
||||||
Gsrv: grpc.NewServer(opts...),
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start causes the server to start accepting incoming connections.
|
|
||||||
// Call Start after registering handlers.
|
|
||||||
func (s *Server) Start() {
|
|
||||||
go s.Gsrv.Serve(s.l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close shuts down the server.
|
|
||||||
func (s *Server) Close() {
|
|
||||||
s.Gsrv.Stop()
|
|
||||||
s.l.Close()
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewServer(t *testing.T) {
|
|
||||||
srv, err := NewServer()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
srv.Start()
|
|
||||||
conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
srv.Close()
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// This file supports generating unique IDs so that multiple test executions
|
|
||||||
// don't interfere with each other, and cleaning up old entities that may
|
|
||||||
// remain if tests exit early.
|
|
||||||
|
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var startTime = time.Now().UTC()
|
|
||||||
|
|
||||||
// A UIDSpace manages a set of unique IDs distinguished by a prefix.
|
|
||||||
type UIDSpace struct {
|
|
||||||
Prefix string
|
|
||||||
re *regexp.Regexp
|
|
||||||
mu sync.Mutex
|
|
||||||
count int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUIDSpace(prefix string) *UIDSpace {
|
|
||||||
return &UIDSpace{
|
|
||||||
Prefix: prefix,
|
|
||||||
re: regexp.MustCompile("^" + regexp.QuoteMeta(prefix) + `-(\d{4})(\d{2})(\d{2})-(\d+)-\d+$`),
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// New generates a new unique ID . The ID consists of the UIDSpace's prefix, a
|
|
||||||
// timestamp, and a counter value. All unique IDs generated in the same test
|
|
||||||
// execution will have the same timestamp.
|
|
||||||
//
|
|
||||||
// Aside from the characters in the prefix, IDs contain only letters, numbers
|
|
||||||
// and hyphens.
|
|
||||||
func (s *UIDSpace) New() string { return s.newID(startTime) }
|
|
||||||
|
|
||||||
func (s *UIDSpace) newID(t time.Time) string {
|
|
||||||
s.mu.Lock()
|
|
||||||
c := s.count
|
|
||||||
s.count++
|
|
||||||
s.mu.Unlock()
|
|
||||||
// Write the time as a date followed by nanoseconds from midnight of that date.
|
|
||||||
// That makes it easier to see the approximate time of the ID when it is displayed.
|
|
||||||
y, m, d := t.Date()
|
|
||||||
ns := t.Sub(time.Date(y, m, d, 0, 0, 0, 0, time.UTC))
|
|
||||||
// Zero-pad the counter for lexical sort order for IDs with the same timestamp.
|
|
||||||
return fmt.Sprintf("%s-%04d%02d%02d-%d-%04d", s.Prefix, y, m, d, ns, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timestamp extracts the timestamp of uid, which must have been generated by
|
|
||||||
// s. The second return value is true on success, false if there was a problem.
|
|
||||||
func (s *UIDSpace) Timestamp(uid string) (time.Time, bool) {
|
|
||||||
subs := s.re.FindStringSubmatch(uid)
|
|
||||||
if subs == nil {
|
|
||||||
return time.Time{}, false
|
|
||||||
}
|
|
||||||
y, err1 := strconv.Atoi(subs[1])
|
|
||||||
m, err2 := strconv.Atoi(subs[2])
|
|
||||||
d, err3 := strconv.Atoi(subs[3])
|
|
||||||
ns, err4 := strconv.Atoi(subs[4])
|
|
||||||
if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
|
|
||||||
return time.Time{}, false
|
|
||||||
}
|
|
||||||
return time.Date(y, time.Month(m), d, 0, 0, 0, ns, time.UTC), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Older reports whether uid was created by m and has a timestamp older than
|
|
||||||
// the current time by at least d.
|
|
||||||
func (s *UIDSpace) Older(uid string, d time.Duration) bool {
|
|
||||||
ts, ok := s.Timestamp(uid)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return time.Since(ts) > d
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package testutil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
s := NewUIDSpace("prefix")
|
|
||||||
tm := time.Date(2017, 1, 6, 0, 0, 0, 21, time.UTC)
|
|
||||||
got := s.newID(tm)
|
|
||||||
want := "prefix-20170106-21-0000"
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("got %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestamp(t *testing.T) {
|
|
||||||
s := NewUIDSpace("unique-ID")
|
|
||||||
uid := s.New()
|
|
||||||
got, ok := s.Timestamp(uid)
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("got ok = false, want true")
|
|
||||||
}
|
|
||||||
if !startTime.Equal(got) {
|
|
||||||
t.Errorf("got %s, want %s", got, startTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
got, ok = s.Timestamp("unique-ID-20160308-123-8")
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("got false, want true")
|
|
||||||
}
|
|
||||||
if want := time.Date(2016, 3, 8, 0, 0, 0, 123, time.UTC); !want.Equal(got) {
|
|
||||||
t.Errorf("got %s, want %s", got, want)
|
|
||||||
}
|
|
||||||
if _, ok = s.Timestamp("invalid-time-1234"); ok {
|
|
||||||
t.Error("got true, want false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOlder(t *testing.T) {
|
|
||||||
s := NewUIDSpace("uid")
|
|
||||||
d := 100 * time.Millisecond
|
|
||||||
id := s.New()
|
|
||||||
|
|
||||||
if got, want := s.Older(id, d), false; got != want {
|
|
||||||
t.Errorf("got %t, want %t", got, want)
|
|
||||||
}
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
if got, want := s.Older(id, d), true; got != want {
|
|
||||||
t.Errorf("got %t, want %t", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A non-matching ID returns false.
|
|
||||||
id2 := NewUIDSpace("different-prefix").New()
|
|
||||||
if got, want := s.Older(id2, d), false; got != want {
|
|
||||||
t.Errorf("got %t, want %t", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
today=$(date +%Y%m%d)
|
|
||||||
|
|
||||||
sed -i -r -e 's/const Repo = "([0-9]{8})"/const Repo = "'$today'"/' $GOFILE
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//go:generate ./update_version.sh
|
|
||||||
|
|
||||||
// Package version contains version information for Google Cloud Client
|
|
||||||
// Libraries for Go, as reported in request headers.
|
|
||||||
package version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Repo is the current version of the client libraries in this
|
|
||||||
// repo. It should be a date in YYYYMMDD format.
|
|
||||||
const Repo = "20170404"
|
|
||||||
|
|
||||||
// Go returns the Go runtime version. The returned string
|
|
||||||
// has no whitespace.
|
|
||||||
func Go() string {
|
|
||||||
return goVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
var goVersion = goVer(runtime.Version())
|
|
||||||
|
|
||||||
const develPrefix = "devel +"
|
|
||||||
|
|
||||||
func goVer(s string) string {
|
|
||||||
if strings.HasPrefix(s, develPrefix) {
|
|
||||||
s = s[len(develPrefix):]
|
|
||||||
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
|
||||||
s = s[:p]
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(s, "go1") {
|
|
||||||
s = s[2:]
|
|
||||||
var prerelease string
|
|
||||||
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
|
|
||||||
s, prerelease = s[:p], s[p:]
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(s, ".") {
|
|
||||||
s += "0"
|
|
||||||
} else if strings.Count(s, ".") < 2 {
|
|
||||||
s += ".0"
|
|
||||||
}
|
|
||||||
if prerelease != "" {
|
|
||||||
s += "-" + prerelease
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func notSemverRune(r rune) bool {
|
|
||||||
return strings.IndexRune("0123456789.", r) < 0
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package version
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestGoVer(t *testing.T) {
|
|
||||||
for _, tst := range []struct {
|
|
||||||
in, want string
|
|
||||||
}{
|
|
||||||
{"go1.8", "1.8.0"},
|
|
||||||
{"go1.7.3", "1.7.3"},
|
|
||||||
{"go1.8.typealias", "1.8.0-typealias"},
|
|
||||||
{"go1.8beta1", "1.8.0-beta1"},
|
|
||||||
{"go1.8rc2", "1.8.0-rc2"},
|
|
||||||
{"devel +824f981dd4b7 Tue Apr 29 21:41:54 2014 -0400", "824f981dd4b7"},
|
|
||||||
{"foo bar zipzap", ""},
|
|
||||||
} {
|
|
||||||
if got := goVer(tst.in); got != tst.want {
|
|
||||||
t.Errorf("goVer(%q) = %q, want %q", tst.in, got, tst.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
# Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
#
|
|
||||||
# Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
# https://github.com/golang/protobuf
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are
|
|
||||||
# met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# * Redistributions in binary form must reproduce the above
|
|
||||||
# copyright notice, this list of conditions and the following disclaimer
|
|
||||||
# in the documentation and/or other materials provided with the
|
|
||||||
# distribution.
|
|
||||||
# * Neither the name of Google Inc. nor the names of its
|
|
||||||
# contributors may be used to endorse or promote products derived from
|
|
||||||
# this software without specific prior written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
install:
|
|
||||||
go install
|
|
||||||
|
|
||||||
test: install generate-test-pbs
|
|
||||||
go test
|
|
||||||
|
|
||||||
|
|
||||||
generate-test-pbs:
|
|
||||||
make install
|
|
||||||
make -C testdata
|
|
||||||
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto
|
|
||||||
make
|
|
2278
vendor/src/github.com/golang/protobuf/proto/all_test.go
vendored
2278
vendor/src/github.com/golang/protobuf/proto/all_test.go
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,300 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
|
|
||||||
pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
testpb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
anypb "github.com/golang/protobuf/ptypes/any"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
expandedMarshaler = proto.TextMarshaler{ExpandAny: true}
|
|
||||||
expandedCompactMarshaler = proto.TextMarshaler{Compact: true, ExpandAny: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
// anyEqual reports whether two messages which may be google.protobuf.Any or may
|
|
||||||
// contain google.protobuf.Any fields are equal. We can't use proto.Equal for
|
|
||||||
// comparison, because semantically equivalent messages may be marshaled to
|
|
||||||
// binary in different tag order. Instead, trust that TextMarshaler with
|
|
||||||
// ExpandAny option works and compare the text marshaling results.
|
|
||||||
func anyEqual(got, want proto.Message) bool {
|
|
||||||
// if messages are proto.Equal, no need to marshal.
|
|
||||||
if proto.Equal(got, want) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
g := expandedMarshaler.Text(got)
|
|
||||||
w := expandedMarshaler.Text(want)
|
|
||||||
return g == w
|
|
||||||
}
|
|
||||||
|
|
||||||
type golden struct {
|
|
||||||
m proto.Message
|
|
||||||
t, c string
|
|
||||||
}
|
|
||||||
|
|
||||||
var goldenMessages = makeGolden()
|
|
||||||
|
|
||||||
func makeGolden() []golden {
|
|
||||||
nested := &pb.Nested{Bunny: "Monty"}
|
|
||||||
nb, err := proto.Marshal(nested)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
m1 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m2 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "http://[::1]/type.googleapis.com/" + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m3 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: `type.googleapis.com/"/` + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m4 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "type.googleapis.com/a/path/" + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m5 := &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb}
|
|
||||||
|
|
||||||
any1 := &testpb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
|
|
||||||
proto.SetExtension(any1, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("foo")})
|
|
||||||
proto.SetExtension(any1, testpb.E_Ext_Text, proto.String("bar"))
|
|
||||||
any1b, err := proto.Marshal(any1)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
any2 := &testpb.MyMessage{Count: proto.Int32(42), Bikeshed: testpb.MyMessage_GREEN.Enum(), RepBytes: [][]byte{[]byte("roboto")}}
|
|
||||||
proto.SetExtension(any2, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("baz")})
|
|
||||||
any2b, err := proto.Marshal(any2)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
m6 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
|
|
||||||
ManyThings: []*anypb.Any{
|
|
||||||
&anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any2), Value: any2b},
|
|
||||||
&anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
m1Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
[type.googleapis.com/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m2Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
["http://[::1]/type.googleapis.com/proto3_proto.Nested"]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m3Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
["type.googleapis.com/\"/proto3_proto.Nested"]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m4Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
[type.googleapis.com/a/path/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m5Golden = `
|
|
||||||
[type.googleapis.com/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m6Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
[type.googleapis.com/testdata.MyMessage]: <
|
|
||||||
count: 47
|
|
||||||
name: "David"
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "foo"
|
|
||||||
>
|
|
||||||
[testdata.Ext.text]: "bar"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
many_things: <
|
|
||||||
[type.googleapis.com/testdata.MyMessage]: <
|
|
||||||
count: 42
|
|
||||||
bikeshed: GREEN
|
|
||||||
rep_bytes: "roboto"
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "baz"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
>
|
|
||||||
many_things: <
|
|
||||||
[type.googleapis.com/testdata.MyMessage]: <
|
|
||||||
count: 47
|
|
||||||
name: "David"
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "foo"
|
|
||||||
>
|
|
||||||
[testdata.Ext.text]: "bar"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
)
|
|
||||||
return []golden{
|
|
||||||
{m1, strings.TrimSpace(m1Golden) + "\n", strings.TrimSpace(compact(m1Golden)) + " "},
|
|
||||||
{m2, strings.TrimSpace(m2Golden) + "\n", strings.TrimSpace(compact(m2Golden)) + " "},
|
|
||||||
{m3, strings.TrimSpace(m3Golden) + "\n", strings.TrimSpace(compact(m3Golden)) + " "},
|
|
||||||
{m4, strings.TrimSpace(m4Golden) + "\n", strings.TrimSpace(compact(m4Golden)) + " "},
|
|
||||||
{m5, strings.TrimSpace(m5Golden) + "\n", strings.TrimSpace(compact(m5Golden)) + " "},
|
|
||||||
{m6, strings.TrimSpace(m6Golden) + "\n", strings.TrimSpace(compact(m6Golden)) + " "},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalGolden(t *testing.T) {
|
|
||||||
for _, tt := range goldenMessages {
|
|
||||||
if got, want := expandedMarshaler.Text(tt.m), tt.t; got != want {
|
|
||||||
t.Errorf("message %v: got:\n%s\nwant:\n%s", tt.m, got, want)
|
|
||||||
}
|
|
||||||
if got, want := expandedCompactMarshaler.Text(tt.m), tt.c; got != want {
|
|
||||||
t.Errorf("message %v: got:\n`%s`\nwant:\n`%s`", tt.m, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalGolden(t *testing.T) {
|
|
||||||
for _, tt := range goldenMessages {
|
|
||||||
want := tt.m
|
|
||||||
got := proto.Clone(tt.m)
|
|
||||||
got.Reset()
|
|
||||||
if err := proto.UnmarshalText(tt.t, got); err != nil {
|
|
||||||
t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.t, err)
|
|
||||||
}
|
|
||||||
if !anyEqual(got, want) {
|
|
||||||
t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.t, got, want)
|
|
||||||
}
|
|
||||||
got.Reset()
|
|
||||||
if err := proto.UnmarshalText(tt.c, got); err != nil {
|
|
||||||
t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.c, err)
|
|
||||||
}
|
|
||||||
if !anyEqual(got, want) {
|
|
||||||
t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.c, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalUnknownAny(t *testing.T) {
|
|
||||||
m := &pb.Message{
|
|
||||||
Anything: &anypb.Any{
|
|
||||||
TypeUrl: "foo",
|
|
||||||
Value: []byte("bar"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
want := `anything: <
|
|
||||||
type_url: "foo"
|
|
||||||
value: "bar"
|
|
||||||
>
|
|
||||||
`
|
|
||||||
got := expandedMarshaler.Text(m)
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("got\n`%s`\nwant\n`%s`", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAmbiguousAny(t *testing.T) {
|
|
||||||
pb := &anypb.Any{}
|
|
||||||
err := proto.UnmarshalText(`
|
|
||||||
type_url: "ttt/proto3_proto.Nested"
|
|
||||||
value: "\n\x05Monty"
|
|
||||||
`, pb)
|
|
||||||
t.Logf("result: %v (error: %v)", expandedMarshaler.Text(pb), err)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to parse ambiguous Any message: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalOverwriteAny(t *testing.T) {
|
|
||||||
pb := &anypb.Any{}
|
|
||||||
err := proto.UnmarshalText(`
|
|
||||||
[type.googleapis.com/a/path/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
[type.googleapis.com/a/path/proto3_proto.Nested]: <
|
|
||||||
bunny: "Rabbit of Caerbannog"
|
|
||||||
>
|
|
||||||
`, pb)
|
|
||||||
want := `line 7: Any message unpacked multiple times, or "type_url" already set`
|
|
||||||
if err.Error() != want {
|
|
||||||
t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalAnyMixAndMatch(t *testing.T) {
|
|
||||||
pb := &anypb.Any{}
|
|
||||||
err := proto.UnmarshalText(`
|
|
||||||
value: "\n\x05Monty"
|
|
||||||
[type.googleapis.com/a/path/proto3_proto.Nested]: <
|
|
||||||
bunny: "Rabbit of Caerbannog"
|
|
||||||
>
|
|
||||||
`, pb)
|
|
||||||
want := `line 5: Any message unpacked multiple times, or "value" already set`
|
|
||||||
if err.Error() != want {
|
|
||||||
t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want)
|
|
||||||
}
|
|
||||||
}
|
|
229
vendor/src/github.com/golang/protobuf/proto/clone.go
vendored
229
vendor/src/github.com/golang/protobuf/proto/clone.go
vendored
|
@ -1,229 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// Protocol buffer deep copy and merge.
|
|
||||||
// TODO: RawMessage.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Clone returns a deep copy of a protocol buffer.
|
|
||||||
func Clone(pb Message) Message {
|
|
||||||
in := reflect.ValueOf(pb)
|
|
||||||
if in.IsNil() {
|
|
||||||
return pb
|
|
||||||
}
|
|
||||||
|
|
||||||
out := reflect.New(in.Type().Elem())
|
|
||||||
// out is empty so a merge is a deep copy.
|
|
||||||
mergeStruct(out.Elem(), in.Elem())
|
|
||||||
return out.Interface().(Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges src into dst.
|
|
||||||
// Required and optional fields that are set in src will be set to that value in dst.
|
|
||||||
// Elements of repeated fields will be appended.
|
|
||||||
// Merge panics if src and dst are not the same type, or if dst is nil.
|
|
||||||
func Merge(dst, src Message) {
|
|
||||||
in := reflect.ValueOf(src)
|
|
||||||
out := reflect.ValueOf(dst)
|
|
||||||
if out.IsNil() {
|
|
||||||
panic("proto: nil destination")
|
|
||||||
}
|
|
||||||
if in.Type() != out.Type() {
|
|
||||||
// Explicit test prior to mergeStruct so that mistyped nils will fail
|
|
||||||
panic("proto: type mismatch")
|
|
||||||
}
|
|
||||||
if in.IsNil() {
|
|
||||||
// Merging nil into non-nil is a quiet no-op
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mergeStruct(out.Elem(), in.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeStruct(out, in reflect.Value) {
|
|
||||||
sprop := GetProperties(in.Type())
|
|
||||||
for i := 0; i < in.NumField(); i++ {
|
|
||||||
f := in.Type().Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
if emIn, ok := extendable(in.Addr().Interface()); ok {
|
|
||||||
emOut, _ := extendable(out.Addr().Interface())
|
|
||||||
mIn, muIn := emIn.extensionsRead()
|
|
||||||
if mIn != nil {
|
|
||||||
mOut := emOut.extensionsWrite()
|
|
||||||
muIn.Lock()
|
|
||||||
mergeExtension(mOut, mIn)
|
|
||||||
muIn.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uf := in.FieldByName("XXX_unrecognized")
|
|
||||||
if !uf.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uin := uf.Bytes()
|
|
||||||
if len(uin) > 0 {
|
|
||||||
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mergeAny performs a merge between two values of the same type.
|
|
||||||
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
|
|
||||||
// prop is set if this is a struct field (it may be nil).
|
|
||||||
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
|
|
||||||
if in.Type() == protoMessageType {
|
|
||||||
if !in.IsNil() {
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
|
|
||||||
} else {
|
|
||||||
Merge(out.Interface().(Message), in.Interface().(Message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch in.Kind() {
|
|
||||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
|
||||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
|
||||||
if !viaPtr && isProto3Zero(in) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
out.Set(in)
|
|
||||||
case reflect.Interface:
|
|
||||||
// Probably a oneof field; copy non-nil values.
|
|
||||||
if in.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Allocate destination if it is not set, or set to a different type.
|
|
||||||
// Otherwise we will merge as normal.
|
|
||||||
if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
|
|
||||||
out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
|
|
||||||
}
|
|
||||||
mergeAny(out.Elem(), in.Elem(), false, nil)
|
|
||||||
case reflect.Map:
|
|
||||||
if in.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.MakeMap(in.Type()))
|
|
||||||
}
|
|
||||||
// For maps with value types of *T or []byte we need to deep copy each value.
|
|
||||||
elemKind := in.Type().Elem().Kind()
|
|
||||||
for _, key := range in.MapKeys() {
|
|
||||||
var val reflect.Value
|
|
||||||
switch elemKind {
|
|
||||||
case reflect.Ptr:
|
|
||||||
val = reflect.New(in.Type().Elem().Elem())
|
|
||||||
mergeAny(val, in.MapIndex(key), false, nil)
|
|
||||||
case reflect.Slice:
|
|
||||||
val = in.MapIndex(key)
|
|
||||||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
|
|
||||||
default:
|
|
||||||
val = in.MapIndex(key)
|
|
||||||
}
|
|
||||||
out.SetMapIndex(key, val)
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if in.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.New(in.Elem().Type()))
|
|
||||||
}
|
|
||||||
mergeAny(out.Elem(), in.Elem(), true, nil)
|
|
||||||
case reflect.Slice:
|
|
||||||
if in.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if in.Type().Elem().Kind() == reflect.Uint8 {
|
|
||||||
// []byte is a scalar bytes field, not a repeated field.
|
|
||||||
|
|
||||||
// Edge case: if this is in a proto3 message, a zero length
|
|
||||||
// bytes field is considered the zero value, and should not
|
|
||||||
// be merged.
|
|
||||||
if prop != nil && prop.proto3 && in.Len() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a deep copy.
|
|
||||||
// Append to []byte{} instead of []byte(nil) so that we never end up
|
|
||||||
// with a nil result.
|
|
||||||
out.SetBytes(append([]byte{}, in.Bytes()...))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n := in.Len()
|
|
||||||
if out.IsNil() {
|
|
||||||
out.Set(reflect.MakeSlice(in.Type(), 0, n))
|
|
||||||
}
|
|
||||||
switch in.Type().Elem().Kind() {
|
|
||||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
|
|
||||||
reflect.String, reflect.Uint32, reflect.Uint64:
|
|
||||||
out.Set(reflect.AppendSlice(out, in))
|
|
||||||
default:
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
x := reflect.Indirect(reflect.New(in.Type().Elem()))
|
|
||||||
mergeAny(x, in.Index(i), false, nil)
|
|
||||||
out.Set(reflect.Append(out, x))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
mergeStruct(out, in)
|
|
||||||
default:
|
|
||||||
// unknown type, so not a protocol buffer
|
|
||||||
log.Printf("proto: don't know how to copy %v", in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeExtension(out, in map[int32]Extension) {
|
|
||||||
for extNum, eIn := range in {
|
|
||||||
eOut := Extension{desc: eIn.desc}
|
|
||||||
if eIn.value != nil {
|
|
||||||
v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
|
|
||||||
mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
|
|
||||||
eOut.value = v.Interface()
|
|
||||||
}
|
|
||||||
if eIn.enc != nil {
|
|
||||||
eOut.enc = make([]byte, len(eIn.enc))
|
|
||||||
copy(eOut.enc, eIn.enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
out[extNum] = eOut
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,300 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cloneTestMessage = &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("niles"),
|
|
||||||
Port: proto.Int32(9099),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Value: []byte("some bytes"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(6),
|
|
||||||
},
|
|
||||||
RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ext := &pb.Ext{
|
|
||||||
Data: proto.String("extension"),
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil {
|
|
||||||
panic("SetExtension: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClone(t *testing.T) {
|
|
||||||
m := proto.Clone(cloneTestMessage).(*pb.MyMessage)
|
|
||||||
if !proto.Equal(m, cloneTestMessage) {
|
|
||||||
t.Errorf("Clone(%v) = %v", cloneTestMessage, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify it was a deep copy.
|
|
||||||
*m.Inner.Port++
|
|
||||||
if proto.Equal(m, cloneTestMessage) {
|
|
||||||
t.Error("Mutating clone changed the original")
|
|
||||||
}
|
|
||||||
// Byte fields and repeated fields should be copied.
|
|
||||||
if &m.Pet[0] == &cloneTestMessage.Pet[0] {
|
|
||||||
t.Error("Pet: repeated field not copied")
|
|
||||||
}
|
|
||||||
if &m.Others[0] == &cloneTestMessage.Others[0] {
|
|
||||||
t.Error("Others: repeated field not copied")
|
|
||||||
}
|
|
||||||
if &m.Others[0].Value[0] == &cloneTestMessage.Others[0].Value[0] {
|
|
||||||
t.Error("Others[0].Value: bytes field not copied")
|
|
||||||
}
|
|
||||||
if &m.RepBytes[0] == &cloneTestMessage.RepBytes[0] {
|
|
||||||
t.Error("RepBytes: repeated field not copied")
|
|
||||||
}
|
|
||||||
if &m.RepBytes[0][0] == &cloneTestMessage.RepBytes[0][0] {
|
|
||||||
t.Error("RepBytes[0]: bytes field not copied")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloneNil(t *testing.T) {
|
|
||||||
var m *pb.MyMessage
|
|
||||||
if c := proto.Clone(m); !proto.Equal(m, c) {
|
|
||||||
t.Errorf("Clone(%v) = %v", m, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mergeTests = []struct {
|
|
||||||
src, dst, want proto.Message
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
src: &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
},
|
|
||||||
dst: &pb.MyMessage{
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
},
|
|
||||||
want: &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &pb.MyMessage{
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("hey"),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
},
|
|
||||||
Pet: []string{"horsey"},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Value: []byte("some bytes"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dst: &pb.MyMessage{
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("niles"),
|
|
||||||
Port: proto.Int32(9099),
|
|
||||||
},
|
|
||||||
Pet: []string{"bunny", "kitty"},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Key: proto.Int64(31415926535),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Explicitly test a src=nil field
|
|
||||||
Inner: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &pb.MyMessage{
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("hey"),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
Port: proto.Int32(9099),
|
|
||||||
},
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Key: proto.Int64(31415926535),
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
Value: []byte("some bytes"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &pb.MyMessage{
|
|
||||||
RepBytes: [][]byte{[]byte("wow")},
|
|
||||||
},
|
|
||||||
dst: &pb.MyMessage{
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(6),
|
|
||||||
},
|
|
||||||
RepBytes: [][]byte{[]byte("sham")},
|
|
||||||
},
|
|
||||||
want: &pb.MyMessage{
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(6),
|
|
||||||
},
|
|
||||||
RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Check that a scalar bytes field replaces rather than appends.
|
|
||||||
{
|
|
||||||
src: &pb.OtherMessage{Value: []byte("foo")},
|
|
||||||
dst: &pb.OtherMessage{Value: []byte("bar")},
|
|
||||||
want: &pb.OtherMessage{Value: []byte("foo")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &pb.MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{6: "Nigel"},
|
|
||||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
|
||||||
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
|
|
||||||
0x4002: &pb.FloatingPoint{
|
|
||||||
F: proto.Float64(2.0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
|
|
||||||
},
|
|
||||||
dst: &pb.MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{
|
|
||||||
6: "Bruce", // should be overwritten
|
|
||||||
7: "Andrew",
|
|
||||||
},
|
|
||||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
|
||||||
0x4002: &pb.FloatingPoint{
|
|
||||||
F: proto.Float64(3.0),
|
|
||||||
Exact: proto.Bool(true),
|
|
||||||
}, // the entire message should be overwritten
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &pb.MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{
|
|
||||||
6: "Nigel",
|
|
||||||
7: "Andrew",
|
|
||||||
},
|
|
||||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
|
||||||
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
|
|
||||||
0x4002: &pb.FloatingPoint{
|
|
||||||
F: proto.Float64(2.0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// proto3 shouldn't merge zero values,
|
|
||||||
// in the same way that proto2 shouldn't merge nils.
|
|
||||||
{
|
|
||||||
src: &proto3pb.Message{
|
|
||||||
Name: "Aaron",
|
|
||||||
Data: []byte(""), // zero value, but not nil
|
|
||||||
},
|
|
||||||
dst: &proto3pb.Message{
|
|
||||||
HeightInCm: 176,
|
|
||||||
Data: []byte("texas!"),
|
|
||||||
},
|
|
||||||
want: &proto3pb.Message{
|
|
||||||
Name: "Aaron",
|
|
||||||
HeightInCm: 176,
|
|
||||||
Data: []byte("texas!"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Oneof fields should merge by assignment.
|
|
||||||
{
|
|
||||||
src: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Number{41},
|
|
||||||
},
|
|
||||||
dst: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Name{"Bobby Tables"},
|
|
||||||
},
|
|
||||||
want: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Number{41},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Oneof nil is the same as not set.
|
|
||||||
{
|
|
||||||
src: &pb.Communique{},
|
|
||||||
dst: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Name{"Bobby Tables"},
|
|
||||||
},
|
|
||||||
want: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Name{"Bobby Tables"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &proto3pb.Message{
|
|
||||||
Terrain: map[string]*proto3pb.Nested{
|
|
||||||
"kay_a": &proto3pb.Nested{Cute: true}, // replace
|
|
||||||
"kay_b": &proto3pb.Nested{Bunny: "rabbit"}, // insert
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dst: &proto3pb.Message{
|
|
||||||
Terrain: map[string]*proto3pb.Nested{
|
|
||||||
"kay_a": &proto3pb.Nested{Bunny: "lost"}, // replaced
|
|
||||||
"kay_c": &proto3pb.Nested{Bunny: "bunny"}, // keep
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &proto3pb.Message{
|
|
||||||
Terrain: map[string]*proto3pb.Nested{
|
|
||||||
"kay_a": &proto3pb.Nested{Cute: true},
|
|
||||||
"kay_b": &proto3pb.Nested{Bunny: "rabbit"},
|
|
||||||
"kay_c": &proto3pb.Nested{Bunny: "bunny"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
|
||||||
for _, m := range mergeTests {
|
|
||||||
got := proto.Clone(m.dst)
|
|
||||||
proto.Merge(got, m.src)
|
|
||||||
if !proto.Equal(got, m.want) {
|
|
||||||
t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,970 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Routines for decoding protocol buffer data to construct in-memory representations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// errOverflow is returned when an integer is too large to be represented.
|
|
||||||
var errOverflow = errors.New("proto: integer overflow")
|
|
||||||
|
|
||||||
// ErrInternalBadWireType is returned by generated code when an incorrect
|
|
||||||
// wire type is encountered. It does not get returned to user code.
|
|
||||||
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
|
||||||
|
|
||||||
// The fundamental decoders that interpret bytes on the wire.
|
|
||||||
// Those that take integer types all return uint64 and are
|
|
||||||
// therefore of type valueDecoder.
|
|
||||||
|
|
||||||
// DecodeVarint reads a varint-encoded integer from the slice.
|
|
||||||
// It returns the integer and the number of bytes consumed, or
|
|
||||||
// zero if there is not enough.
|
|
||||||
// This is the format for the
|
|
||||||
// int32, int64, uint32, uint64, bool, and enum
|
|
||||||
// protocol buffer types.
|
|
||||||
func DecodeVarint(buf []byte) (x uint64, n int) {
|
|
||||||
for shift := uint(0); shift < 64; shift += 7 {
|
|
||||||
if n >= len(buf) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
b := uint64(buf[n])
|
|
||||||
n++
|
|
||||||
x |= (b & 0x7F) << shift
|
|
||||||
if (b & 0x80) == 0 {
|
|
||||||
return x, n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number is too large to represent in a 64-bit value.
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
|
|
||||||
i := p.index
|
|
||||||
l := len(p.buf)
|
|
||||||
|
|
||||||
for shift := uint(0); shift < 64; shift += 7 {
|
|
||||||
if i >= l {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b := p.buf[i]
|
|
||||||
i++
|
|
||||||
x |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
p.index = i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number is too large to represent in a 64-bit value.
|
|
||||||
err = errOverflow
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeVarint reads a varint-encoded integer from the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// int32, int64, uint32, uint64, bool, and enum
|
|
||||||
// protocol buffer types.
|
|
||||||
func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
|
||||||
i := p.index
|
|
||||||
buf := p.buf
|
|
||||||
|
|
||||||
if i >= len(buf) {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
} else if buf[i] < 0x80 {
|
|
||||||
p.index++
|
|
||||||
return uint64(buf[i]), nil
|
|
||||||
} else if len(buf)-i < 10 {
|
|
||||||
return p.decodeVarintSlow()
|
|
||||||
}
|
|
||||||
|
|
||||||
var b uint64
|
|
||||||
// we already checked the first byte
|
|
||||||
x = uint64(buf[i]) - 0x80
|
|
||||||
i++
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 7
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 7
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 14
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 14
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 21
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 21
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 28
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 28
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 35
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 35
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 42
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 42
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 49
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 49
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 56
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
x -= 0x80 << 56
|
|
||||||
|
|
||||||
b = uint64(buf[i])
|
|
||||||
i++
|
|
||||||
x += b << 63
|
|
||||||
if b&0x80 == 0 {
|
|
||||||
goto done
|
|
||||||
}
|
|
||||||
// x -= 0x80 << 63 // Always zero.
|
|
||||||
|
|
||||||
return 0, errOverflow
|
|
||||||
|
|
||||||
done:
|
|
||||||
p.index = i
|
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFixed64 reads a 64-bit integer from the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// fixed64, sfixed64, and double protocol buffer types.
|
|
||||||
func (p *Buffer) DecodeFixed64() (x uint64, err error) {
|
|
||||||
// x, err already 0
|
|
||||||
i := p.index + 8
|
|
||||||
if i < 0 || i > len(p.buf) {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.index = i
|
|
||||||
|
|
||||||
x = uint64(p.buf[i-8])
|
|
||||||
x |= uint64(p.buf[i-7]) << 8
|
|
||||||
x |= uint64(p.buf[i-6]) << 16
|
|
||||||
x |= uint64(p.buf[i-5]) << 24
|
|
||||||
x |= uint64(p.buf[i-4]) << 32
|
|
||||||
x |= uint64(p.buf[i-3]) << 40
|
|
||||||
x |= uint64(p.buf[i-2]) << 48
|
|
||||||
x |= uint64(p.buf[i-1]) << 56
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFixed32 reads a 32-bit integer from the Buffer.
|
|
||||||
// This is the format for the
|
|
||||||
// fixed32, sfixed32, and float protocol buffer types.
|
|
||||||
func (p *Buffer) DecodeFixed32() (x uint64, err error) {
|
|
||||||
// x, err already 0
|
|
||||||
i := p.index + 4
|
|
||||||
if i < 0 || i > len(p.buf) {
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.index = i
|
|
||||||
|
|
||||||
x = uint64(p.buf[i-4])
|
|
||||||
x |= uint64(p.buf[i-3]) << 8
|
|
||||||
x |= uint64(p.buf[i-2]) << 16
|
|
||||||
x |= uint64(p.buf[i-1]) << 24
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
|
|
||||||
// from the Buffer.
|
|
||||||
// This is the format used for the sint64 protocol buffer type.
|
|
||||||
func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
|
|
||||||
x, err = p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
|
|
||||||
// from the Buffer.
|
|
||||||
// This is the format used for the sint32 protocol buffer type.
|
|
||||||
func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
|
|
||||||
x, err = p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are not ValueDecoders: they produce an array of bytes or a string.
|
|
||||||
// bytes, embedded messages
|
|
||||||
|
|
||||||
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
|
|
||||||
// This is the format used for the bytes protocol buffer
|
|
||||||
// type and for embedded messages.
|
|
||||||
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
|
|
||||||
n, err := p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nb := int(n)
|
|
||||||
if nb < 0 {
|
|
||||||
return nil, fmt.Errorf("proto: bad byte length %d", nb)
|
|
||||||
}
|
|
||||||
end := p.index + nb
|
|
||||||
if end < p.index || end > len(p.buf) {
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if !alloc {
|
|
||||||
// todo: check if can get more uses of alloc=false
|
|
||||||
buf = p.buf[p.index:end]
|
|
||||||
p.index += nb
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = make([]byte, nb)
|
|
||||||
copy(buf, p.buf[p.index:])
|
|
||||||
p.index += nb
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeStringBytes reads an encoded string from the Buffer.
|
|
||||||
// This is the format used for the proto2 string type.
|
|
||||||
func (p *Buffer) DecodeStringBytes() (s string, err error) {
|
|
||||||
buf, err := p.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
|
||||||
// If the protocol buffer has extensions, and the field matches, add it as an extension.
|
|
||||||
// Otherwise, if the XXX_unrecognized field exists, append the skipped data there.
|
|
||||||
func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error {
|
|
||||||
oi := o.index
|
|
||||||
|
|
||||||
err := o.skip(t, tag, wire)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unrecField.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr := structPointer_Bytes(base, unrecField)
|
|
||||||
|
|
||||||
// Add the skipped field to struct field
|
|
||||||
obuf := o.buf
|
|
||||||
|
|
||||||
o.buf = *ptr
|
|
||||||
o.EncodeVarint(uint64(tag<<3 | wire))
|
|
||||||
*ptr = append(o.buf, obuf[oi:o.index]...)
|
|
||||||
|
|
||||||
o.buf = obuf
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
|
|
||||||
func (o *Buffer) skip(t reflect.Type, tag, wire int) error {
|
|
||||||
|
|
||||||
var u uint64
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch wire {
|
|
||||||
case WireVarint:
|
|
||||||
_, err = o.DecodeVarint()
|
|
||||||
case WireFixed64:
|
|
||||||
_, err = o.DecodeFixed64()
|
|
||||||
case WireBytes:
|
|
||||||
_, err = o.DecodeRawBytes(false)
|
|
||||||
case WireFixed32:
|
|
||||||
_, err = o.DecodeFixed32()
|
|
||||||
case WireStartGroup:
|
|
||||||
for {
|
|
||||||
u, err = o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fwire := int(u & 0x7)
|
|
||||||
if fwire == WireEndGroup {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ftag := int(u >> 3)
|
|
||||||
err = o.skip(t, ftag, fwire)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is the interface representing objects that can
|
|
||||||
// unmarshal themselves. The method should reset the receiver before
|
|
||||||
// decoding starts. The argument points to data that may be
|
|
||||||
// overwritten, so implementations should not keep references to the
|
|
||||||
// buffer.
|
|
||||||
type Unmarshaler interface {
|
|
||||||
Unmarshal([]byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses the protocol buffer representation in buf and places the
|
|
||||||
// decoded result in pb. If the struct underlying pb does not match
|
|
||||||
// the data in buf, the results can be unpredictable.
|
|
||||||
//
|
|
||||||
// Unmarshal resets pb before starting to unmarshal, so any
|
|
||||||
// existing data in pb is always removed. Use UnmarshalMerge
|
|
||||||
// to preserve and append to existing data.
|
|
||||||
func Unmarshal(buf []byte, pb Message) error {
|
|
||||||
pb.Reset()
|
|
||||||
return UnmarshalMerge(buf, pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMerge parses the protocol buffer representation in buf and
|
|
||||||
// writes the decoded result to pb. If the struct underlying pb does not match
|
|
||||||
// the data in buf, the results can be unpredictable.
|
|
||||||
//
|
|
||||||
// UnmarshalMerge merges into existing data in pb.
|
|
||||||
// Most code should use Unmarshal instead.
|
|
||||||
func UnmarshalMerge(buf []byte, pb Message) error {
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
|
||||||
return u.Unmarshal(buf)
|
|
||||||
}
|
|
||||||
return NewBuffer(buf).Unmarshal(pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeMessage reads a count-delimited message from the Buffer.
|
|
||||||
func (p *Buffer) DecodeMessage(pb Message) error {
|
|
||||||
enc, err := p.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return NewBuffer(enc).Unmarshal(pb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeGroup reads a tag-delimited group from the Buffer.
|
|
||||||
func (p *Buffer) DecodeGroup(pb Message) error {
|
|
||||||
typ, base, err := getbase(pb)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses the protocol buffer representation in the
|
|
||||||
// Buffer and places the decoded result in pb. If the struct
|
|
||||||
// underlying pb does not match the data in the buffer, the results can be
|
|
||||||
// unpredictable.
|
|
||||||
//
|
|
||||||
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
|
|
||||||
func (p *Buffer) Unmarshal(pb Message) error {
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if u, ok := pb.(Unmarshaler); ok {
|
|
||||||
err := u.Unmarshal(p.buf[p.index:])
|
|
||||||
p.index = len(p.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, base, err := getbase(pb)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
|
|
||||||
|
|
||||||
if collectStats {
|
|
||||||
stats.Decode++
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmarshalType does the work of unmarshaling a structure.
|
|
||||||
func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error {
|
|
||||||
var state errorState
|
|
||||||
required, reqFields := prop.reqCount, uint64(0)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
for err == nil && o.index < len(o.buf) {
|
|
||||||
oi := o.index
|
|
||||||
var u uint64
|
|
||||||
u, err = o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
wire := int(u & 0x7)
|
|
||||||
if wire == WireEndGroup {
|
|
||||||
if is_group {
|
|
||||||
if required > 0 {
|
|
||||||
// Not enough information to determine the exact field.
|
|
||||||
// (See below.)
|
|
||||||
return &RequiredNotSetError{"{Unknown}"}
|
|
||||||
}
|
|
||||||
return nil // input is satisfied
|
|
||||||
}
|
|
||||||
return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
|
|
||||||
}
|
|
||||||
tag := int(u >> 3)
|
|
||||||
if tag <= 0 {
|
|
||||||
return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire)
|
|
||||||
}
|
|
||||||
fieldnum, ok := prop.decoderTags.get(tag)
|
|
||||||
if !ok {
|
|
||||||
// Maybe it's an extension?
|
|
||||||
if prop.extendable {
|
|
||||||
if e, _ := extendable(structPointer_Interface(base, st)); isExtensionField(e, int32(tag)) {
|
|
||||||
if err = o.skip(st, tag, wire); err == nil {
|
|
||||||
extmap := e.extensionsWrite()
|
|
||||||
ext := extmap[int32(tag)] // may be missing
|
|
||||||
ext.enc = append(ext.enc, o.buf[oi:o.index]...)
|
|
||||||
extmap[int32(tag)] = ext
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Maybe it's a oneof?
|
|
||||||
if prop.oneofUnmarshaler != nil {
|
|
||||||
m := structPointer_Interface(base, st).(Message)
|
|
||||||
// First return value indicates whether tag is a oneof field.
|
|
||||||
ok, err = prop.oneofUnmarshaler(m, tag, wire, o)
|
|
||||||
if err == ErrInternalBadWireType {
|
|
||||||
// Map the error to something more descriptive.
|
|
||||||
// Do the formatting here to save generated code space.
|
|
||||||
err = fmt.Errorf("bad wiretype for oneof field in %T", m)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = o.skipAndSave(st, tag, wire, base, prop.unrecField)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p := prop.Prop[fieldnum]
|
|
||||||
|
|
||||||
if p.dec == nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dec := p.dec
|
|
||||||
if wire != WireStartGroup && wire != p.WireType {
|
|
||||||
if wire == WireBytes && p.packedDec != nil {
|
|
||||||
// a packable field
|
|
||||||
dec = p.packedDec
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decErr := dec(o, p, base)
|
|
||||||
if decErr != nil && !state.shouldContinue(decErr, p) {
|
|
||||||
err = decErr
|
|
||||||
}
|
|
||||||
if err == nil && p.Required {
|
|
||||||
// Successfully decoded a required field.
|
|
||||||
if tag <= 64 {
|
|
||||||
// use bitmap for fields 1-64 to catch field reuse.
|
|
||||||
var mask uint64 = 1 << uint64(tag-1)
|
|
||||||
if reqFields&mask == 0 {
|
|
||||||
// new required field
|
|
||||||
reqFields |= mask
|
|
||||||
required--
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is imprecise. It can be fooled by a required field
|
|
||||||
// with a tag > 64 that is encoded twice; that's very rare.
|
|
||||||
// A fully correct implementation would require allocating
|
|
||||||
// a data structure, which we would like to avoid.
|
|
||||||
required--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if is_group {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
if state.err != nil {
|
|
||||||
return state.err
|
|
||||||
}
|
|
||||||
if required > 0 {
|
|
||||||
// Not enough information to determine the exact field. If we use extra
|
|
||||||
// CPU, we could determine the field only if the missing required field
|
|
||||||
// has a tag <= 64 and we check reqFields.
|
|
||||||
return &RequiredNotSetError{"{Unknown}"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Individual type decoders
|
|
||||||
// For each,
|
|
||||||
// u is the decoded value,
|
|
||||||
// v is a pointer to the field (pointer) in the struct
|
|
||||||
|
|
||||||
// Sizes of the pools to allocate inside the Buffer.
|
|
||||||
// The goal is modest amortization and allocation
|
|
||||||
// on at least 16-byte boundaries.
|
|
||||||
const (
|
|
||||||
boolPoolSize = 16
|
|
||||||
uint32PoolSize = 8
|
|
||||||
uint64PoolSize = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Decode a bool.
|
|
||||||
func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(o.bools) == 0 {
|
|
||||||
o.bools = make([]bool, boolPoolSize)
|
|
||||||
}
|
|
||||||
o.bools[0] = u != 0
|
|
||||||
*structPointer_Bool(base, p.field) = &o.bools[0]
|
|
||||||
o.bools = o.bools[1:]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_BoolVal(base, p.field) = u != 0
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode an int32.
|
|
||||||
func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word32_Set(structPointer_Word32(base, p.field), o, uint32(u))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode an int64.
|
|
||||||
func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word64_Set(structPointer_Word64(base, p.field), o, u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a string.
|
|
||||||
func (o *Buffer) dec_string(p *Properties, base structPointer) error {
|
|
||||||
s, err := o.DecodeStringBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_String(base, p.field) = &s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
|
|
||||||
s, err := o.DecodeStringBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_StringVal(base, p.field) = s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of bytes ([]byte).
|
|
||||||
func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error {
|
|
||||||
b, err := o.DecodeRawBytes(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*structPointer_Bytes(base, p.field) = b
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of bools ([]bool).
|
|
||||||
func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := structPointer_BoolSlice(base, p.field)
|
|
||||||
*v = append(*v, u != 0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of bools ([]bool) in packed format.
|
|
||||||
func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error {
|
|
||||||
v := structPointer_BoolSlice(base, p.field)
|
|
||||||
|
|
||||||
nn, err := o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nb := int(nn) // number of bytes of encoded bools
|
|
||||||
fin := o.index + nb
|
|
||||||
if fin < o.index {
|
|
||||||
return errOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
y := *v
|
|
||||||
for o.index < fin {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
y = append(y, u != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
*v = y
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int32s ([]int32).
|
|
||||||
func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
structPointer_Word32Slice(base, p.field).Append(uint32(u))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int32s ([]int32) in packed format.
|
|
||||||
func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error {
|
|
||||||
v := structPointer_Word32Slice(base, p.field)
|
|
||||||
|
|
||||||
nn, err := o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nb := int(nn) // number of bytes of encoded int32s
|
|
||||||
|
|
||||||
fin := o.index + nb
|
|
||||||
if fin < o.index {
|
|
||||||
return errOverflow
|
|
||||||
}
|
|
||||||
for o.index < fin {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Append(uint32(u))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int64s ([]int64).
|
|
||||||
func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
structPointer_Word64Slice(base, p.field).Append(u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of int64s ([]int64) in packed format.
|
|
||||||
func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error {
|
|
||||||
v := structPointer_Word64Slice(base, p.field)
|
|
||||||
|
|
||||||
nn, err := o.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nb := int(nn) // number of bytes of encoded int64s
|
|
||||||
|
|
||||||
fin := o.index + nb
|
|
||||||
if fin < o.index {
|
|
||||||
return errOverflow
|
|
||||||
}
|
|
||||||
for o.index < fin {
|
|
||||||
u, err := p.valDec(o)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Append(u)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of strings ([]string).
|
|
||||||
func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error {
|
|
||||||
s, err := o.DecodeStringBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := structPointer_StringSlice(base, p.field)
|
|
||||||
*v = append(*v, s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of slice of bytes ([][]byte).
|
|
||||||
func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
|
|
||||||
b, err := o.DecodeRawBytes(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v := structPointer_BytesSlice(base, p.field)
|
|
||||||
*v = append(*v, b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a map field.
|
|
||||||
func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
|
|
||||||
raw, err := o.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
oi := o.index // index at the end of this map entry
|
|
||||||
o.index -= len(raw) // move buffer back to start of map entry
|
|
||||||
|
|
||||||
mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V
|
|
||||||
if mptr.Elem().IsNil() {
|
|
||||||
mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
|
|
||||||
}
|
|
||||||
v := mptr.Elem() // map[K]V
|
|
||||||
|
|
||||||
// Prepare addressable doubly-indirect placeholders for the key and value types.
|
|
||||||
// See enc_new_map for why.
|
|
||||||
keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
|
|
||||||
keybase := toStructPointer(keyptr.Addr()) // **K
|
|
||||||
|
|
||||||
var valbase structPointer
|
|
||||||
var valptr reflect.Value
|
|
||||||
switch p.mtype.Elem().Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
// []byte
|
|
||||||
var dummy []byte
|
|
||||||
valptr = reflect.ValueOf(&dummy) // *[]byte
|
|
||||||
valbase = toStructPointer(valptr) // *[]byte
|
|
||||||
case reflect.Ptr:
|
|
||||||
// message; valptr is **Msg; need to allocate the intermediate pointer
|
|
||||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
|
||||||
valptr.Set(reflect.New(valptr.Type().Elem()))
|
|
||||||
valbase = toStructPointer(valptr)
|
|
||||||
default:
|
|
||||||
// everything else
|
|
||||||
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
|
|
||||||
valbase = toStructPointer(valptr.Addr()) // **V
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode.
|
|
||||||
// This parses a restricted wire format, namely the encoding of a message
|
|
||||||
// with two fields. See enc_new_map for the format.
|
|
||||||
for o.index < oi {
|
|
||||||
// tagcode for key and value properties are always a single byte
|
|
||||||
// because they have tags 1 and 2.
|
|
||||||
tagcode := o.buf[o.index]
|
|
||||||
o.index++
|
|
||||||
switch tagcode {
|
|
||||||
case p.mkeyprop.tagcode[0]:
|
|
||||||
if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case p.mvalprop.tagcode[0]:
|
|
||||||
if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// TODO: Should we silently skip this instead?
|
|
||||||
return fmt.Errorf("proto: bad map data tag %d", raw[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyelem, valelem := keyptr.Elem(), valptr.Elem()
|
|
||||||
if !keyelem.IsValid() {
|
|
||||||
keyelem = reflect.Zero(p.mtype.Key())
|
|
||||||
}
|
|
||||||
if !valelem.IsValid() {
|
|
||||||
valelem = reflect.Zero(p.mtype.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
v.SetMapIndex(keyelem, valelem)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a group.
|
|
||||||
func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
|
|
||||||
bas := structPointer_GetStructPointer(base, p.field)
|
|
||||||
if structPointer_IsNil(bas) {
|
|
||||||
// allocate new nested message
|
|
||||||
bas = toStructPointer(reflect.New(p.stype))
|
|
||||||
structPointer_SetStructPointer(base, p.field, bas)
|
|
||||||
}
|
|
||||||
return o.unmarshalType(p.stype, p.sprop, true, bas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode an embedded message.
|
|
||||||
func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) {
|
|
||||||
raw, e := o.DecodeRawBytes(false)
|
|
||||||
if e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
bas := structPointer_GetStructPointer(base, p.field)
|
|
||||||
if structPointer_IsNil(bas) {
|
|
||||||
// allocate new nested message
|
|
||||||
bas = toStructPointer(reflect.New(p.stype))
|
|
||||||
structPointer_SetStructPointer(base, p.field, bas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if p.isUnmarshaler {
|
|
||||||
iv := structPointer_Interface(bas, p.stype)
|
|
||||||
return iv.(Unmarshaler).Unmarshal(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
obuf := o.buf
|
|
||||||
oi := o.index
|
|
||||||
o.buf = raw
|
|
||||||
o.index = 0
|
|
||||||
|
|
||||||
err = o.unmarshalType(p.stype, p.sprop, false, bas)
|
|
||||||
o.buf = obuf
|
|
||||||
o.index = oi
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of embedded messages.
|
|
||||||
func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error {
|
|
||||||
return o.dec_slice_struct(p, false, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of embedded groups.
|
|
||||||
func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error {
|
|
||||||
return o.dec_slice_struct(p, true, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a slice of structs ([]*struct).
|
|
||||||
func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error {
|
|
||||||
v := reflect.New(p.stype)
|
|
||||||
bas := toStructPointer(v)
|
|
||||||
structPointer_StructPointerSlice(base, p.field).Append(bas)
|
|
||||||
|
|
||||||
if is_group {
|
|
||||||
err := o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := o.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the object can unmarshal itself, let it.
|
|
||||||
if p.isUnmarshaler {
|
|
||||||
iv := v.Interface()
|
|
||||||
return iv.(Unmarshaler).Unmarshal(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
obuf := o.buf
|
|
||||||
oi := o.index
|
|
||||||
o.buf = raw
|
|
||||||
o.index = 0
|
|
||||||
|
|
||||||
err = o.unmarshalType(p.stype, p.sprop, is_group, bas)
|
|
||||||
|
|
||||||
o.buf = obuf
|
|
||||||
o.index = oi
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,256 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
tpb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bytesBlackhole []byte
|
|
||||||
msgBlackhole = new(tpb.Message)
|
|
||||||
)
|
|
||||||
|
|
||||||
// BenchmarkVarint32ArraySmall shows the performance on an array of small int32 fields (1 and
|
|
||||||
// 2 bytes long).
|
|
||||||
func BenchmarkVarint32ArraySmall(b *testing.B) {
|
|
||||||
for i := uint(1); i <= 10; i++ {
|
|
||||||
dist := genInt32Dist([7]int{0, 3, 1}, 1<<i)
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{
|
|
||||||
ShortKey: dist,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
|
|
||||||
scratchBuf := proto.NewBuffer(nil)
|
|
||||||
b.ResetTimer()
|
|
||||||
for k := 0; k < b.N; k++ {
|
|
||||||
scratchBuf.SetBuf(raw)
|
|
||||||
msgBlackhole.Reset()
|
|
||||||
if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
|
|
||||||
b.Error("wrong decode", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkVarint32ArrayLarge shows the performance on an array of large int32 fields (3 and
|
|
||||||
// 4 bytes long, with a small number of 1, 2, 5 and 10 byte long versions).
|
|
||||||
func BenchmarkVarint32ArrayLarge(b *testing.B) {
|
|
||||||
for i := uint(1); i <= 10; i++ {
|
|
||||||
dist := genInt32Dist([7]int{0, 1, 2, 4, 8, 1, 1}, 1<<i)
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{
|
|
||||||
ShortKey: dist,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
|
|
||||||
scratchBuf := proto.NewBuffer(nil)
|
|
||||||
b.ResetTimer()
|
|
||||||
for k := 0; k < b.N; k++ {
|
|
||||||
scratchBuf.SetBuf(raw)
|
|
||||||
msgBlackhole.Reset()
|
|
||||||
if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
|
|
||||||
b.Error("wrong decode", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkVarint64ArraySmall shows the performance on an array of small int64 fields (1 and
|
|
||||||
// 2 bytes long).
|
|
||||||
func BenchmarkVarint64ArraySmall(b *testing.B) {
|
|
||||||
for i := uint(1); i <= 10; i++ {
|
|
||||||
dist := genUint64Dist([11]int{0, 3, 1}, 1<<i)
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{
|
|
||||||
Key: dist,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
|
|
||||||
scratchBuf := proto.NewBuffer(nil)
|
|
||||||
b.ResetTimer()
|
|
||||||
for k := 0; k < b.N; k++ {
|
|
||||||
scratchBuf.SetBuf(raw)
|
|
||||||
msgBlackhole.Reset()
|
|
||||||
if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
|
|
||||||
b.Error("wrong decode", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkVarint64ArrayLarge shows the performance on an array of large int64 fields (6, 7,
|
|
||||||
// and 8 bytes long with a small number of the other sizes).
|
|
||||||
func BenchmarkVarint64ArrayLarge(b *testing.B) {
|
|
||||||
for i := uint(1); i <= 10; i++ {
|
|
||||||
dist := genUint64Dist([11]int{0, 1, 1, 2, 4, 8, 16, 32, 16, 1, 1}, 1<<i)
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{
|
|
||||||
Key: dist,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
b.Run(fmt.Sprintf("Len%v", len(dist)), func(b *testing.B) {
|
|
||||||
scratchBuf := proto.NewBuffer(nil)
|
|
||||||
b.ResetTimer()
|
|
||||||
for k := 0; k < b.N; k++ {
|
|
||||||
scratchBuf.SetBuf(raw)
|
|
||||||
msgBlackhole.Reset()
|
|
||||||
if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
|
|
||||||
b.Error("wrong decode", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkVarint64ArrayMixed shows the performance of lots of small messages, each
|
|
||||||
// containing a small number of large (3, 4, and 5 byte) repeated int64s.
|
|
||||||
func BenchmarkVarint64ArrayMixed(b *testing.B) {
|
|
||||||
for i := uint(1); i <= 1<<5; i <<= 1 {
|
|
||||||
dist := genUint64Dist([11]int{0, 0, 0, 4, 6, 4, 0, 0, 0, 0, 0}, int(i))
|
|
||||||
// number of sub fields
|
|
||||||
for k := uint(1); k <= 1<<10; k <<= 2 {
|
|
||||||
msg := &tpb.Message{}
|
|
||||||
for m := uint(0); m < k; m++ {
|
|
||||||
msg.Children = append(msg.Children, &tpb.Message{
|
|
||||||
Key: dist,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
raw, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
b.Run(fmt.Sprintf("Fields%vLen%v", k, i), func(b *testing.B) {
|
|
||||||
scratchBuf := proto.NewBuffer(nil)
|
|
||||||
b.ResetTimer()
|
|
||||||
for k := 0; k < b.N; k++ {
|
|
||||||
scratchBuf.SetBuf(raw)
|
|
||||||
msgBlackhole.Reset()
|
|
||||||
if err := scratchBuf.Unmarshal(msgBlackhole); err != nil {
|
|
||||||
b.Error("wrong decode", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// genInt32Dist generates a slice of ints that will match the size distribution of dist.
|
|
||||||
// A size of 6 corresponds to a max length varint32, which is 10 bytes. The distribution
|
|
||||||
// is 1-indexed. (i.e. the value at index 1 is how many 1 byte ints to create).
|
|
||||||
func genInt32Dist(dist [7]int, count int) (dest []int32) {
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
for k := 0; k < len(dist); k++ {
|
|
||||||
var num int32
|
|
||||||
switch k {
|
|
||||||
case 1:
|
|
||||||
num = 1<<7 - 1
|
|
||||||
case 2:
|
|
||||||
num = 1<<14 - 1
|
|
||||||
case 3:
|
|
||||||
num = 1<<21 - 1
|
|
||||||
case 4:
|
|
||||||
num = 1<<28 - 1
|
|
||||||
case 5:
|
|
||||||
num = 1<<29 - 1
|
|
||||||
case 6:
|
|
||||||
num = -1
|
|
||||||
}
|
|
||||||
for m := 0; m < dist[k]; m++ {
|
|
||||||
dest = append(dest, num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// genUint64Dist generates a slice of ints that will match the size distribution of dist.
|
|
||||||
// The distribution is 1-indexed. (i.e. the value at index 1 is how many 1 byte ints to create).
|
|
||||||
func genUint64Dist(dist [11]int, count int) (dest []uint64) {
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
for k := 0; k < len(dist); k++ {
|
|
||||||
var num uint64
|
|
||||||
switch k {
|
|
||||||
case 1:
|
|
||||||
num = 1<<7 - 1
|
|
||||||
case 2:
|
|
||||||
num = 1<<14 - 1
|
|
||||||
case 3:
|
|
||||||
num = 1<<21 - 1
|
|
||||||
case 4:
|
|
||||||
num = 1<<28 - 1
|
|
||||||
case 5:
|
|
||||||
num = 1<<35 - 1
|
|
||||||
case 6:
|
|
||||||
num = 1<<42 - 1
|
|
||||||
case 7:
|
|
||||||
num = 1<<49 - 1
|
|
||||||
case 8:
|
|
||||||
num = 1<<56 - 1
|
|
||||||
case 9:
|
|
||||||
num = 1<<63 - 1
|
|
||||||
case 10:
|
|
||||||
num = 1<<64 - 1
|
|
||||||
}
|
|
||||||
for m := 0; m < dist[k]; m++ {
|
|
||||||
dest = append(dest, num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkDecodeEmpty measures the overhead of doing the minimal possible decode.
|
|
||||||
func BenchmarkDecodeEmpty(b *testing.B) {
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if err := proto.Unmarshal(raw, msgBlackhole); err != nil {
|
|
||||||
b.Error("wrong decode", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1362
vendor/src/github.com/golang/protobuf/proto/encode.go
vendored
1362
vendor/src/github.com/golang/protobuf/proto/encode.go
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,83 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
tpb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
blackhole []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
// BenchmarkAny creates increasingly large arbitrary Any messages. The type is always the
|
|
||||||
// same.
|
|
||||||
func BenchmarkAny(b *testing.B) {
|
|
||||||
data := make([]byte, 1<<20)
|
|
||||||
quantum := 1 << 10
|
|
||||||
for i := uint(0); i <= 10; i++ {
|
|
||||||
b.Run(strconv.Itoa(quantum<<i), func(b *testing.B) {
|
|
||||||
for k := 0; k < b.N; k++ {
|
|
||||||
inner := &tpb.Message{
|
|
||||||
Data: data[:quantum<<i],
|
|
||||||
}
|
|
||||||
outer, err := ptypes.MarshalAny(inner)
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{
|
|
||||||
Anything: outer,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
blackhole = raw
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkEmpy measures the overhead of doing the minimal possible encode.
|
|
||||||
func BenchmarkEmpy(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
raw, err := proto.Marshal(&tpb.Message{})
|
|
||||||
if err != nil {
|
|
||||||
b.Error("wrong encode", err)
|
|
||||||
}
|
|
||||||
blackhole = raw
|
|
||||||
}
|
|
||||||
}
|
|
300
vendor/src/github.com/golang/protobuf/proto/equal.go
vendored
300
vendor/src/github.com/golang/protobuf/proto/equal.go
vendored
|
@ -1,300 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// Protocol buffer comparison.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Equal returns true iff protocol buffers a and b are equal.
|
|
||||||
The arguments must both be pointers to protocol buffer structs.
|
|
||||||
|
|
||||||
Equality is defined in this way:
|
|
||||||
- Two messages are equal iff they are the same type,
|
|
||||||
corresponding fields are equal, unknown field sets
|
|
||||||
are equal, and extensions sets are equal.
|
|
||||||
- Two set scalar fields are equal iff their values are equal.
|
|
||||||
If the fields are of a floating-point type, remember that
|
|
||||||
NaN != x for all x, including NaN. If the message is defined
|
|
||||||
in a proto3 .proto file, fields are not "set"; specifically,
|
|
||||||
zero length proto3 "bytes" fields are equal (nil == {}).
|
|
||||||
- Two repeated fields are equal iff their lengths are the same,
|
|
||||||
and their corresponding elements are equal. Note a "bytes" field,
|
|
||||||
although represented by []byte, is not a repeated field and the
|
|
||||||
rule for the scalar fields described above applies.
|
|
||||||
- Two unset fields are equal.
|
|
||||||
- Two unknown field sets are equal if their current
|
|
||||||
encoded state is equal.
|
|
||||||
- Two extension sets are equal iff they have corresponding
|
|
||||||
elements that are pairwise equal.
|
|
||||||
- Two map fields are equal iff their lengths are the same,
|
|
||||||
and they contain the same set of elements. Zero-length map
|
|
||||||
fields are equal.
|
|
||||||
- Every other combination of things are not equal.
|
|
||||||
|
|
||||||
The return value is undefined if a and b are not protocol buffers.
|
|
||||||
*/
|
|
||||||
func Equal(a, b Message) bool {
|
|
||||||
if a == nil || b == nil {
|
|
||||||
return a == b
|
|
||||||
}
|
|
||||||
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
|
|
||||||
if v1.Type() != v2.Type() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if v1.Kind() == reflect.Ptr {
|
|
||||||
if v1.IsNil() {
|
|
||||||
return v2.IsNil()
|
|
||||||
}
|
|
||||||
if v2.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
v1, v2 = v1.Elem(), v2.Elem()
|
|
||||||
}
|
|
||||||
if v1.Kind() != reflect.Struct {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return equalStruct(v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// v1 and v2 are known to have the same type.
|
|
||||||
func equalStruct(v1, v2 reflect.Value) bool {
|
|
||||||
sprop := GetProperties(v1.Type())
|
|
||||||
for i := 0; i < v1.NumField(); i++ {
|
|
||||||
f := v1.Type().Field(i)
|
|
||||||
if strings.HasPrefix(f.Name, "XXX_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f1, f2 := v1.Field(i), v2.Field(i)
|
|
||||||
if f.Type.Kind() == reflect.Ptr {
|
|
||||||
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
|
|
||||||
// both unset
|
|
||||||
continue
|
|
||||||
} else if n1 != n2 {
|
|
||||||
// set/unset mismatch
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
b1, ok := f1.Interface().(raw)
|
|
||||||
if ok {
|
|
||||||
b2 := f2.Interface().(raw)
|
|
||||||
// RawMessage
|
|
||||||
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
f1, f2 = f1.Elem(), f2.Elem()
|
|
||||||
}
|
|
||||||
if !equalAny(f1, f2, sprop.Prop[i]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() {
|
|
||||||
em2 := v2.FieldByName("XXX_InternalExtensions")
|
|
||||||
if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
|
|
||||||
em2 := v2.FieldByName("XXX_extensions")
|
|
||||||
if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uf := v1.FieldByName("XXX_unrecognized")
|
|
||||||
if !uf.IsValid() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
u1 := uf.Bytes()
|
|
||||||
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
|
|
||||||
if !bytes.Equal(u1, u2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// v1 and v2 are known to have the same type.
|
|
||||||
// prop may be nil.
|
|
||||||
func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
|
|
||||||
if v1.Type() == protoMessageType {
|
|
||||||
m1, _ := v1.Interface().(Message)
|
|
||||||
m2, _ := v2.Interface().(Message)
|
|
||||||
return Equal(m1, m2)
|
|
||||||
}
|
|
||||||
switch v1.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return v1.Bool() == v2.Bool()
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v1.Float() == v2.Float()
|
|
||||||
case reflect.Int32, reflect.Int64:
|
|
||||||
return v1.Int() == v2.Int()
|
|
||||||
case reflect.Interface:
|
|
||||||
// Probably a oneof field; compare the inner values.
|
|
||||||
n1, n2 := v1.IsNil(), v2.IsNil()
|
|
||||||
if n1 || n2 {
|
|
||||||
return n1 == n2
|
|
||||||
}
|
|
||||||
e1, e2 := v1.Elem(), v2.Elem()
|
|
||||||
if e1.Type() != e2.Type() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return equalAny(e1, e2, nil)
|
|
||||||
case reflect.Map:
|
|
||||||
if v1.Len() != v2.Len() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, key := range v1.MapKeys() {
|
|
||||||
val2 := v2.MapIndex(key)
|
|
||||||
if !val2.IsValid() {
|
|
||||||
// This key was not found in the second map.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !equalAny(v1.MapIndex(key), val2, nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case reflect.Ptr:
|
|
||||||
// Maps may have nil values in them, so check for nil.
|
|
||||||
if v1.IsNil() && v2.IsNil() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if v1.IsNil() != v2.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return equalAny(v1.Elem(), v2.Elem(), prop)
|
|
||||||
case reflect.Slice:
|
|
||||||
if v1.Type().Elem().Kind() == reflect.Uint8 {
|
|
||||||
// short circuit: []byte
|
|
||||||
|
|
||||||
// Edge case: if this is in a proto3 message, a zero length
|
|
||||||
// bytes field is considered the zero value.
|
|
||||||
if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if v1.IsNil() != v2.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v1.Len() != v2.Len() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := 0; i < v1.Len(); i++ {
|
|
||||||
if !equalAny(v1.Index(i), v2.Index(i), prop) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case reflect.String:
|
|
||||||
return v1.Interface().(string) == v2.Interface().(string)
|
|
||||||
case reflect.Struct:
|
|
||||||
return equalStruct(v1, v2)
|
|
||||||
case reflect.Uint32, reflect.Uint64:
|
|
||||||
return v1.Uint() == v2.Uint()
|
|
||||||
}
|
|
||||||
|
|
||||||
// unknown type, so not a protocol buffer
|
|
||||||
log.Printf("proto: don't know how to compare %v", v1)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// base is the struct type that the extensions are based on.
|
|
||||||
// x1 and x2 are InternalExtensions.
|
|
||||||
func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool {
|
|
||||||
em1, _ := x1.extensionsRead()
|
|
||||||
em2, _ := x2.extensionsRead()
|
|
||||||
return equalExtMap(base, em1, em2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
|
||||||
if len(em1) != len(em2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for extNum, e1 := range em1 {
|
|
||||||
e2, ok := em2[extNum]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
m1, m2 := e1.value, e2.value
|
|
||||||
|
|
||||||
if m1 != nil && m2 != nil {
|
|
||||||
// Both are unencoded.
|
|
||||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// At least one is encoded. To do a semantically correct comparison
|
|
||||||
// we need to unmarshal them first.
|
|
||||||
var desc *ExtensionDesc
|
|
||||||
if m := extensionMaps[base]; m != nil {
|
|
||||||
desc = m[extNum]
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if m1 == nil {
|
|
||||||
m1, err = decodeExtension(e1.enc, desc)
|
|
||||||
}
|
|
||||||
if m2 == nil && err == nil {
|
|
||||||
m2, err = decodeExtension(e2.enc, desc)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// The encoded form is invalid.
|
|
||||||
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,224 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/golang/protobuf/proto"
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Four identical base messages.
|
|
||||||
// The init function adds extensions to some of them.
|
|
||||||
var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
|
|
||||||
// Two messages with non-message extensions.
|
|
||||||
var messageWithInt32Extension1 = &pb.MyMessage{Count: Int32(8)}
|
|
||||||
var messageWithInt32Extension2 = &pb.MyMessage{Count: Int32(8)}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ext1 := &pb.Ext{Data: String("Kirk")}
|
|
||||||
ext2 := &pb.Ext{Data: String("Picard")}
|
|
||||||
|
|
||||||
// messageWithExtension1a has ext1, but never marshals it.
|
|
||||||
if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
panic("SetExtension on 1a failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// messageWithExtension1b is the unmarshaled form of messageWithExtension1a.
|
|
||||||
if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
panic("SetExtension on 1b failed: " + err.Error())
|
|
||||||
}
|
|
||||||
buf, err := Marshal(messageWithExtension1b)
|
|
||||||
if err != nil {
|
|
||||||
panic("Marshal of 1b failed: " + err.Error())
|
|
||||||
}
|
|
||||||
messageWithExtension1b.Reset()
|
|
||||||
if err := Unmarshal(buf, messageWithExtension1b); err != nil {
|
|
||||||
panic("Unmarshal of 1b failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// messageWithExtension2 has ext2.
|
|
||||||
if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil {
|
|
||||||
panic("SetExtension on 2 failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(23)); err != nil {
|
|
||||||
panic("SetExtension on Int32-1 failed: " + err.Error())
|
|
||||||
}
|
|
||||||
if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(24)); err != nil {
|
|
||||||
panic("SetExtension on Int32-2 failed: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var EqualTests = []struct {
|
|
||||||
desc string
|
|
||||||
a, b Message
|
|
||||||
exp bool
|
|
||||||
}{
|
|
||||||
{"different types", &pb.GoEnum{}, &pb.GoTestField{}, false},
|
|
||||||
{"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true},
|
|
||||||
{"nil vs nil", nil, nil, true},
|
|
||||||
{"typed nil vs typed nil", (*pb.GoEnum)(nil), (*pb.GoEnum)(nil), true},
|
|
||||||
{"typed nil vs empty", (*pb.GoEnum)(nil), &pb.GoEnum{}, false},
|
|
||||||
{"different typed nil", (*pb.GoEnum)(nil), (*pb.GoTestField)(nil), false},
|
|
||||||
|
|
||||||
{"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false},
|
|
||||||
{"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false},
|
|
||||||
{"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false},
|
|
||||||
{"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true},
|
|
||||||
|
|
||||||
{"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false},
|
|
||||||
{"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false},
|
|
||||||
{"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false},
|
|
||||||
{"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true},
|
|
||||||
{"repeated, nil equal nil", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: nil}, true},
|
|
||||||
{"repeated, nil equal empty", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: []int32{}}, true},
|
|
||||||
{"repeated, empty equal nil", &pb.GoTest{F_Int32Repeated: []int32{}}, &pb.GoTest{F_Int32Repeated: nil}, true},
|
|
||||||
|
|
||||||
{
|
|
||||||
"nested, different",
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}},
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested, equal",
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{"bytes", &pb.OtherMessage{Value: []byte("foo")}, &pb.OtherMessage{Value: []byte("foo")}, true},
|
|
||||||
{"bytes, empty", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: []byte{}}, true},
|
|
||||||
{"bytes, empty vs nil", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: nil}, false},
|
|
||||||
{
|
|
||||||
"repeated bytes",
|
|
||||||
&pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
|
|
||||||
&pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
// In proto3, []byte{} and []byte(nil) are equal.
|
|
||||||
{"proto3 bytes, empty vs nil", &proto3pb.Message{Data: []byte{}}, &proto3pb.Message{Data: nil}, true},
|
|
||||||
|
|
||||||
{"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false},
|
|
||||||
{"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true},
|
|
||||||
{"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false},
|
|
||||||
|
|
||||||
{"int32 extension vs. itself", messageWithInt32Extension1, messageWithInt32Extension1, true},
|
|
||||||
{"int32 extension vs. a different int32", messageWithInt32Extension1, messageWithInt32Extension2, false},
|
|
||||||
|
|
||||||
{
|
|
||||||
"message with group",
|
|
||||||
&pb.MyMessage{
|
|
||||||
Count: Int32(1),
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: Int32(5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&pb.MyMessage{
|
|
||||||
Count: Int32(1),
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: Int32(5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"map same",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"map different entry",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"map different key only",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"map different value only",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"zero-length maps same",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{}},
|
|
||||||
&pb.MessageWithMap{NameMapping: nil},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"orders in map don't matter",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken", 2: "Rob"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob", 1: "Ken"}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"oneof same",
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"oneof one nil",
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
&pb.Communique{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"oneof different",
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
&pb.Communique{Union: &pb.Communique_Name{"Bobby Tables"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqual(t *testing.T) {
|
|
||||||
for _, tc := range EqualTests {
|
|
||||||
if res := Equal(tc.a, tc.b); res != tc.exp {
|
|
||||||
t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,587 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Types and routines for supporting protocol buffer extensions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
|
|
||||||
var ErrMissingExtension = errors.New("proto: missing extension")
|
|
||||||
|
|
||||||
// ExtensionRange represents a range of message extensions for a protocol buffer.
|
|
||||||
// Used in code generated by the protocol compiler.
|
|
||||||
type ExtensionRange struct {
|
|
||||||
Start, End int32 // both inclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
// extendableProto is an interface implemented by any protocol buffer generated by the current
|
|
||||||
// proto compiler that may be extended.
|
|
||||||
type extendableProto interface {
|
|
||||||
Message
|
|
||||||
ExtensionRangeArray() []ExtensionRange
|
|
||||||
extensionsWrite() map[int32]Extension
|
|
||||||
extensionsRead() (map[int32]Extension, sync.Locker)
|
|
||||||
}
|
|
||||||
|
|
||||||
// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous
|
|
||||||
// version of the proto compiler that may be extended.
|
|
||||||
type extendableProtoV1 interface {
|
|
||||||
Message
|
|
||||||
ExtensionRangeArray() []ExtensionRange
|
|
||||||
ExtensionMap() map[int32]Extension
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto.
|
|
||||||
type extensionAdapter struct {
|
|
||||||
extendableProtoV1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e extensionAdapter) extensionsWrite() map[int32]Extension {
|
|
||||||
return e.ExtensionMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) {
|
|
||||||
return e.ExtensionMap(), notLocker{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notLocker is a sync.Locker whose Lock and Unlock methods are nops.
|
|
||||||
type notLocker struct{}
|
|
||||||
|
|
||||||
func (n notLocker) Lock() {}
|
|
||||||
func (n notLocker) Unlock() {}
|
|
||||||
|
|
||||||
// extendable returns the extendableProto interface for the given generated proto message.
|
|
||||||
// If the proto message has the old extension format, it returns a wrapper that implements
|
|
||||||
// the extendableProto interface.
|
|
||||||
func extendable(p interface{}) (extendableProto, bool) {
|
|
||||||
if ep, ok := p.(extendableProto); ok {
|
|
||||||
return ep, ok
|
|
||||||
}
|
|
||||||
if ep, ok := p.(extendableProtoV1); ok {
|
|
||||||
return extensionAdapter{ep}, ok
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX_InternalExtensions is an internal representation of proto extensions.
|
|
||||||
//
|
|
||||||
// Each generated message struct type embeds an anonymous XXX_InternalExtensions field,
|
|
||||||
// thus gaining the unexported 'extensions' method, which can be called only from the proto package.
|
|
||||||
//
|
|
||||||
// The methods of XXX_InternalExtensions are not concurrency safe in general,
|
|
||||||
// but calls to logically read-only methods such as has and get may be executed concurrently.
|
|
||||||
type XXX_InternalExtensions struct {
|
|
||||||
// The struct must be indirect so that if a user inadvertently copies a
|
|
||||||
// generated message and its embedded XXX_InternalExtensions, they
|
|
||||||
// avoid the mayhem of a copied mutex.
|
|
||||||
//
|
|
||||||
// The mutex serializes all logically read-only operations to p.extensionMap.
|
|
||||||
// It is up to the client to ensure that write operations to p.extensionMap are
|
|
||||||
// mutually exclusive with other accesses.
|
|
||||||
p *struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
extensionMap map[int32]Extension
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensionsWrite returns the extension map, creating it on first use.
|
|
||||||
func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension {
|
|
||||||
if e.p == nil {
|
|
||||||
e.p = new(struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
extensionMap map[int32]Extension
|
|
||||||
})
|
|
||||||
e.p.extensionMap = make(map[int32]Extension)
|
|
||||||
}
|
|
||||||
return e.p.extensionMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensionsRead returns the extensions map for read-only use. It may be nil.
|
|
||||||
// The caller must hold the returned mutex's lock when accessing Elements within the map.
|
|
||||||
func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) {
|
|
||||||
if e.p == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return e.p.extensionMap, &e.p.mu
|
|
||||||
}
|
|
||||||
|
|
||||||
var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
|
|
||||||
var extendableProtoV1Type = reflect.TypeOf((*extendableProtoV1)(nil)).Elem()
|
|
||||||
|
|
||||||
// ExtensionDesc represents an extension specification.
|
|
||||||
// Used in generated code from the protocol compiler.
|
|
||||||
type ExtensionDesc struct {
|
|
||||||
ExtendedType Message // nil pointer to the type that is being extended
|
|
||||||
ExtensionType interface{} // nil pointer to the extension type
|
|
||||||
Field int32 // field number
|
|
||||||
Name string // fully-qualified name of extension, for text formatting
|
|
||||||
Tag string // protobuf tag style
|
|
||||||
Filename string // name of the file in which the extension is defined
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ed *ExtensionDesc) repeated() bool {
|
|
||||||
t := reflect.TypeOf(ed.ExtensionType)
|
|
||||||
return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extension represents an extension in a message.
|
|
||||||
type Extension struct {
|
|
||||||
// When an extension is stored in a message using SetExtension
|
|
||||||
// only desc and value are set. When the message is marshaled
|
|
||||||
// enc will be set to the encoded form of the message.
|
|
||||||
//
|
|
||||||
// When a message is unmarshaled and contains extensions, each
|
|
||||||
// extension will have only enc set. When such an extension is
|
|
||||||
// accessed using GetExtension (or GetExtensions) desc and value
|
|
||||||
// will be set.
|
|
||||||
desc *ExtensionDesc
|
|
||||||
value interface{}
|
|
||||||
enc []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawExtension is for testing only.
|
|
||||||
func SetRawExtension(base Message, id int32, b []byte) {
|
|
||||||
epb, ok := extendable(base)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
extmap := epb.extensionsWrite()
|
|
||||||
extmap[id] = Extension{enc: b}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isExtensionField returns true iff the given field number is in an extension range.
|
|
||||||
func isExtensionField(pb extendableProto, field int32) bool {
|
|
||||||
for _, er := range pb.ExtensionRangeArray() {
|
|
||||||
if er.Start <= field && field <= er.End {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkExtensionTypes checks that the given extension is valid for pb.
|
|
||||||
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
|
|
||||||
var pbi interface{} = pb
|
|
||||||
// Check the extended type.
|
|
||||||
if ea, ok := pbi.(extensionAdapter); ok {
|
|
||||||
pbi = ea.extendableProtoV1
|
|
||||||
}
|
|
||||||
if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
|
|
||||||
return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
|
|
||||||
}
|
|
||||||
// Check the range.
|
|
||||||
if !isExtensionField(pb, extension.Field) {
|
|
||||||
return errors.New("proto: bad extension number; not in declared ranges")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extPropKey is sufficient to uniquely identify an extension.
|
|
||||||
type extPropKey struct {
|
|
||||||
base reflect.Type
|
|
||||||
field int32
|
|
||||||
}
|
|
||||||
|
|
||||||
var extProp = struct {
|
|
||||||
sync.RWMutex
|
|
||||||
m map[extPropKey]*Properties
|
|
||||||
}{
|
|
||||||
m: make(map[extPropKey]*Properties),
|
|
||||||
}
|
|
||||||
|
|
||||||
func extensionProperties(ed *ExtensionDesc) *Properties {
|
|
||||||
key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
|
|
||||||
|
|
||||||
extProp.RLock()
|
|
||||||
if prop, ok := extProp.m[key]; ok {
|
|
||||||
extProp.RUnlock()
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
extProp.RUnlock()
|
|
||||||
|
|
||||||
extProp.Lock()
|
|
||||||
defer extProp.Unlock()
|
|
||||||
// Check again.
|
|
||||||
if prop, ok := extProp.m[key]; ok {
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
prop := new(Properties)
|
|
||||||
prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
|
|
||||||
extProp.m[key] = prop
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode encodes any unmarshaled (unencoded) extensions in e.
|
|
||||||
func encodeExtensions(e *XXX_InternalExtensions) error {
|
|
||||||
m, mu := e.extensionsRead()
|
|
||||||
if m == nil {
|
|
||||||
return nil // fast path
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
return encodeExtensionsMap(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode encodes any unmarshaled (unencoded) extensions in e.
|
|
||||||
func encodeExtensionsMap(m map[int32]Extension) error {
|
|
||||||
for k, e := range m {
|
|
||||||
if e.value == nil || e.desc == nil {
|
|
||||||
// Extension is only in its encoded form.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't skip extensions that have an encoded form set,
|
|
||||||
// because the extension value may have been mutated after
|
|
||||||
// the last time this function was called.
|
|
||||||
|
|
||||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
|
||||||
props := extensionProperties(e.desc)
|
|
||||||
|
|
||||||
p := NewBuffer(nil)
|
|
||||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
|
||||||
// Pass a *T with a zero field and hope it all works out.
|
|
||||||
x := reflect.New(et)
|
|
||||||
x.Elem().Set(reflect.ValueOf(e.value))
|
|
||||||
if err := props.enc(p, props, toStructPointer(x)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.enc = p.buf
|
|
||||||
m[k] = e
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extensionsSize(e *XXX_InternalExtensions) (n int) {
|
|
||||||
m, mu := e.extensionsRead()
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
return extensionsMapSize(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func extensionsMapSize(m map[int32]Extension) (n int) {
|
|
||||||
for _, e := range m {
|
|
||||||
if e.value == nil || e.desc == nil {
|
|
||||||
// Extension is only in its encoded form.
|
|
||||||
n += len(e.enc)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't skip extensions that have an encoded form set,
|
|
||||||
// because the extension value may have been mutated after
|
|
||||||
// the last time this function was called.
|
|
||||||
|
|
||||||
et := reflect.TypeOf(e.desc.ExtensionType)
|
|
||||||
props := extensionProperties(e.desc)
|
|
||||||
|
|
||||||
// If e.value has type T, the encoder expects a *struct{ X T }.
|
|
||||||
// Pass a *T with a zero field and hope it all works out.
|
|
||||||
x := reflect.New(et)
|
|
||||||
x.Elem().Set(reflect.ValueOf(e.value))
|
|
||||||
n += props.size(props, toStructPointer(x))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasExtension returns whether the given extension is present in pb.
|
|
||||||
func HasExtension(pb Message, extension *ExtensionDesc) bool {
|
|
||||||
// TODO: Check types, field numbers, etc.?
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
extmap, mu := epb.extensionsRead()
|
|
||||||
if extmap == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
_, ok = extmap[extension.Field]
|
|
||||||
mu.Unlock()
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearExtension removes the given extension from pb.
|
|
||||||
func ClearExtension(pb Message, extension *ExtensionDesc) {
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: Check types, field numbers, etc.?
|
|
||||||
extmap := epb.extensionsWrite()
|
|
||||||
delete(extmap, extension.Field)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExtension parses and returns the given extension of pb.
|
|
||||||
// If the extension is not present and has no default value it returns ErrMissingExtension.
|
|
||||||
func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("proto: not an extendable proto")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkExtensionTypes(epb, extension); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
emap, mu := epb.extensionsRead()
|
|
||||||
if emap == nil {
|
|
||||||
return defaultExtensionValue(extension)
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
e, ok := emap[extension.Field]
|
|
||||||
if !ok {
|
|
||||||
// defaultExtensionValue returns the default value or
|
|
||||||
// ErrMissingExtension if there is no default.
|
|
||||||
return defaultExtensionValue(extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.value != nil {
|
|
||||||
// Already decoded. Check the descriptor, though.
|
|
||||||
if e.desc != extension {
|
|
||||||
// This shouldn't happen. If it does, it means that
|
|
||||||
// GetExtension was called twice with two different
|
|
||||||
// descriptors with the same field number.
|
|
||||||
return nil, errors.New("proto: descriptor conflict")
|
|
||||||
}
|
|
||||||
return e.value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := decodeExtension(e.enc, extension)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember the decoded version and drop the encoded version.
|
|
||||||
// That way it is safe to mutate what we return.
|
|
||||||
e.value = v
|
|
||||||
e.desc = extension
|
|
||||||
e.enc = nil
|
|
||||||
emap[extension.Field] = e
|
|
||||||
return e.value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// defaultExtensionValue returns the default value for extension.
|
|
||||||
// If no default for an extension is defined ErrMissingExtension is returned.
|
|
||||||
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
|
|
||||||
t := reflect.TypeOf(extension.ExtensionType)
|
|
||||||
props := extensionProperties(extension)
|
|
||||||
|
|
||||||
sf, _, err := fieldDefault(t, props)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if sf == nil || sf.value == nil {
|
|
||||||
// There is no default value.
|
|
||||||
return nil, ErrMissingExtension
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Kind() != reflect.Ptr {
|
|
||||||
// We do not need to return a Ptr, we can directly return sf.value.
|
|
||||||
return sf.value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to return an interface{} that is a pointer to sf.value.
|
|
||||||
value := reflect.New(t).Elem()
|
|
||||||
value.Set(reflect.New(value.Type().Elem()))
|
|
||||||
if sf.kind == reflect.Int32 {
|
|
||||||
// We may have an int32 or an enum, but the underlying data is int32.
|
|
||||||
// Since we can't set an int32 into a non int32 reflect.value directly
|
|
||||||
// set it as a int32.
|
|
||||||
value.Elem().SetInt(int64(sf.value.(int32)))
|
|
||||||
} else {
|
|
||||||
value.Elem().Set(reflect.ValueOf(sf.value))
|
|
||||||
}
|
|
||||||
return value.Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeExtension decodes an extension encoded in b.
|
|
||||||
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
|
|
||||||
o := NewBuffer(b)
|
|
||||||
|
|
||||||
t := reflect.TypeOf(extension.ExtensionType)
|
|
||||||
|
|
||||||
props := extensionProperties(extension)
|
|
||||||
|
|
||||||
// t is a pointer to a struct, pointer to basic type or a slice.
|
|
||||||
// Allocate a "field" to store the pointer/slice itself; the
|
|
||||||
// pointer/slice will be stored here. We pass
|
|
||||||
// the address of this field to props.dec.
|
|
||||||
// This passes a zero field and a *t and lets props.dec
|
|
||||||
// interpret it as a *struct{ x t }.
|
|
||||||
value := reflect.New(t).Elem()
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Discard wire type and field number varint. It isn't needed.
|
|
||||||
if _, err := o.DecodeVarint(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.index >= len(o.buf) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value.Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
|
|
||||||
// The returned slice has the same length as es; missing extensions will appear as nil elements.
|
|
||||||
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("proto: not an extendable proto")
|
|
||||||
}
|
|
||||||
extensions = make([]interface{}, len(es))
|
|
||||||
for i, e := range es {
|
|
||||||
extensions[i], err = GetExtension(epb, e)
|
|
||||||
if err == ErrMissingExtension {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
|
|
||||||
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
|
|
||||||
// just the Field field, which defines the extension's field number.
|
|
||||||
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
|
|
||||||
}
|
|
||||||
registeredExtensions := RegisteredExtensions(pb)
|
|
||||||
|
|
||||||
emap, mu := epb.extensionsRead()
|
|
||||||
if emap == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
extensions := make([]*ExtensionDesc, 0, len(emap))
|
|
||||||
for extid, e := range emap {
|
|
||||||
desc := e.desc
|
|
||||||
if desc == nil {
|
|
||||||
desc = registeredExtensions[extid]
|
|
||||||
if desc == nil {
|
|
||||||
desc = &ExtensionDesc{Field: extid}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extensions = append(extensions, desc)
|
|
||||||
}
|
|
||||||
return extensions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetExtension sets the specified extension of pb to the specified value.
|
|
||||||
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("proto: not an extendable proto")
|
|
||||||
}
|
|
||||||
if err := checkExtensionTypes(epb, extension); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
typ := reflect.TypeOf(extension.ExtensionType)
|
|
||||||
if typ != reflect.TypeOf(value) {
|
|
||||||
return errors.New("proto: bad extension value type")
|
|
||||||
}
|
|
||||||
// nil extension values need to be caught early, because the
|
|
||||||
// encoder can't distinguish an ErrNil due to a nil extension
|
|
||||||
// from an ErrNil due to a missing field. Extensions are
|
|
||||||
// always optional, so the encoder would just swallow the error
|
|
||||||
// and drop all the extensions from the encoded message.
|
|
||||||
if reflect.ValueOf(value).IsNil() {
|
|
||||||
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
extmap := epb.extensionsWrite()
|
|
||||||
extmap[extension.Field] = Extension{desc: extension, value: value}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearAllExtensions clears all extensions from pb.
|
|
||||||
func ClearAllExtensions(pb Message) {
|
|
||||||
epb, ok := extendable(pb)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m := epb.extensionsWrite()
|
|
||||||
for k := range m {
|
|
||||||
delete(m, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A global registry of extensions.
|
|
||||||
// The generated code will register the generated descriptors by calling RegisterExtension.
|
|
||||||
|
|
||||||
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
|
|
||||||
|
|
||||||
// RegisterExtension is called from the generated code.
|
|
||||||
func RegisterExtension(desc *ExtensionDesc) {
|
|
||||||
st := reflect.TypeOf(desc.ExtendedType).Elem()
|
|
||||||
m := extensionMaps[st]
|
|
||||||
if m == nil {
|
|
||||||
m = make(map[int32]*ExtensionDesc)
|
|
||||||
extensionMaps[st] = m
|
|
||||||
}
|
|
||||||
if _, ok := m[desc.Field]; ok {
|
|
||||||
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
|
|
||||||
}
|
|
||||||
m[desc.Field] = desc
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisteredExtensions returns a map of the registered extensions of a
|
|
||||||
// protocol buffer struct, indexed by the extension number.
|
|
||||||
// The argument pb should be a nil pointer to the struct type.
|
|
||||||
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
|
||||||
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
|
||||||
}
|
|
|
@ -1,536 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetExtensionsWithMissingExtensions(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{}
|
|
||||||
ext1 := &pb.Ext{}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
t.Fatalf("Could not set ext1: %s", err)
|
|
||||||
}
|
|
||||||
exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{
|
|
||||||
pb.E_Ext_More,
|
|
||||||
pb.E_Ext_Text,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetExtensions() failed: %s", err)
|
|
||||||
}
|
|
||||||
if exts[0] != ext1 {
|
|
||||||
t.Errorf("ext1 not in returned extensions: %T %v", exts[0], exts[0])
|
|
||||||
}
|
|
||||||
if exts[1] != nil {
|
|
||||||
t.Errorf("ext2 in returned extensions: %T %v", exts[1], exts[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtensionDescsWithMissingExtensions(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{Count: proto.Int32(0)}
|
|
||||||
extdesc1 := pb.E_Ext_More
|
|
||||||
if descs, err := proto.ExtensionDescs(msg); len(descs) != 0 || err != nil {
|
|
||||||
t.Errorf("proto.ExtensionDescs: got %d descs, error %v; want 0, nil", len(descs), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext1 := &pb.Ext{}
|
|
||||||
if err := proto.SetExtension(msg, extdesc1, ext1); err != nil {
|
|
||||||
t.Fatalf("Could not set ext1: %s", err)
|
|
||||||
}
|
|
||||||
extdesc2 := &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*pb.MyMessage)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 123456789,
|
|
||||||
Name: "a.b",
|
|
||||||
Tag: "varint,123456789,opt",
|
|
||||||
}
|
|
||||||
ext2 := proto.Bool(false)
|
|
||||||
if err := proto.SetExtension(msg, extdesc2, ext2); err != nil {
|
|
||||||
t.Fatalf("Could not set ext2: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not marshal msg: %v", err)
|
|
||||||
}
|
|
||||||
if err := proto.Unmarshal(b, msg); err != nil {
|
|
||||||
t.Fatalf("Could not unmarshal into msg: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
descs, err := proto.ExtensionDescs(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("proto.ExtensionDescs: got error %v", err)
|
|
||||||
}
|
|
||||||
sortExtDescs(descs)
|
|
||||||
wantDescs := []*proto.ExtensionDesc{extdesc1, &proto.ExtensionDesc{Field: extdesc2.Field}}
|
|
||||||
if !reflect.DeepEqual(descs, wantDescs) {
|
|
||||||
t.Errorf("proto.ExtensionDescs(msg) sorted extension ids: got %+v, want %+v", descs, wantDescs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExtensionDescSlice []*proto.ExtensionDesc
|
|
||||||
|
|
||||||
func (s ExtensionDescSlice) Len() int { return len(s) }
|
|
||||||
func (s ExtensionDescSlice) Less(i, j int) bool { return s[i].Field < s[j].Field }
|
|
||||||
func (s ExtensionDescSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
func sortExtDescs(s []*proto.ExtensionDesc) {
|
|
||||||
sort.Sort(ExtensionDescSlice(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetExtensionStability(t *testing.T) {
|
|
||||||
check := func(m *pb.MyMessage) bool {
|
|
||||||
ext1, err := proto.GetExtension(m, pb.E_Ext_More)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetExtension() failed: %s", err)
|
|
||||||
}
|
|
||||||
ext2, err := proto.GetExtension(m, pb.E_Ext_More)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetExtension() failed: %s", err)
|
|
||||||
}
|
|
||||||
return ext1 == ext2
|
|
||||||
}
|
|
||||||
msg := &pb.MyMessage{Count: proto.Int32(4)}
|
|
||||||
ext0 := &pb.Ext{}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil {
|
|
||||||
t.Fatalf("Could not set ext1: %s", ext0)
|
|
||||||
}
|
|
||||||
if !check(msg) {
|
|
||||||
t.Errorf("GetExtension() not stable before marshaling")
|
|
||||||
}
|
|
||||||
bb, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Marshal() failed: %s", err)
|
|
||||||
}
|
|
||||||
msg1 := &pb.MyMessage{}
|
|
||||||
err = proto.Unmarshal(bb, msg1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unmarshal() failed: %s", err)
|
|
||||||
}
|
|
||||||
if !check(msg1) {
|
|
||||||
t.Errorf("GetExtension() not stable after unmarshaling")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetExtensionDefaults(t *testing.T) {
|
|
||||||
var setFloat64 float64 = 1
|
|
||||||
var setFloat32 float32 = 2
|
|
||||||
var setInt32 int32 = 3
|
|
||||||
var setInt64 int64 = 4
|
|
||||||
var setUint32 uint32 = 5
|
|
||||||
var setUint64 uint64 = 6
|
|
||||||
var setBool = true
|
|
||||||
var setBool2 = false
|
|
||||||
var setString = "Goodnight string"
|
|
||||||
var setBytes = []byte("Goodnight bytes")
|
|
||||||
var setEnum = pb.DefaultsMessage_TWO
|
|
||||||
|
|
||||||
type testcase struct {
|
|
||||||
ext *proto.ExtensionDesc // Extension we are testing.
|
|
||||||
want interface{} // Expected value of extension, or nil (meaning that GetExtension will fail).
|
|
||||||
def interface{} // Expected value of extension after ClearExtension().
|
|
||||||
}
|
|
||||||
tests := []testcase{
|
|
||||||
{pb.E_NoDefaultDouble, setFloat64, nil},
|
|
||||||
{pb.E_NoDefaultFloat, setFloat32, nil},
|
|
||||||
{pb.E_NoDefaultInt32, setInt32, nil},
|
|
||||||
{pb.E_NoDefaultInt64, setInt64, nil},
|
|
||||||
{pb.E_NoDefaultUint32, setUint32, nil},
|
|
||||||
{pb.E_NoDefaultUint64, setUint64, nil},
|
|
||||||
{pb.E_NoDefaultSint32, setInt32, nil},
|
|
||||||
{pb.E_NoDefaultSint64, setInt64, nil},
|
|
||||||
{pb.E_NoDefaultFixed32, setUint32, nil},
|
|
||||||
{pb.E_NoDefaultFixed64, setUint64, nil},
|
|
||||||
{pb.E_NoDefaultSfixed32, setInt32, nil},
|
|
||||||
{pb.E_NoDefaultSfixed64, setInt64, nil},
|
|
||||||
{pb.E_NoDefaultBool, setBool, nil},
|
|
||||||
{pb.E_NoDefaultBool, setBool2, nil},
|
|
||||||
{pb.E_NoDefaultString, setString, nil},
|
|
||||||
{pb.E_NoDefaultBytes, setBytes, nil},
|
|
||||||
{pb.E_NoDefaultEnum, setEnum, nil},
|
|
||||||
{pb.E_DefaultDouble, setFloat64, float64(3.1415)},
|
|
||||||
{pb.E_DefaultFloat, setFloat32, float32(3.14)},
|
|
||||||
{pb.E_DefaultInt32, setInt32, int32(42)},
|
|
||||||
{pb.E_DefaultInt64, setInt64, int64(43)},
|
|
||||||
{pb.E_DefaultUint32, setUint32, uint32(44)},
|
|
||||||
{pb.E_DefaultUint64, setUint64, uint64(45)},
|
|
||||||
{pb.E_DefaultSint32, setInt32, int32(46)},
|
|
||||||
{pb.E_DefaultSint64, setInt64, int64(47)},
|
|
||||||
{pb.E_DefaultFixed32, setUint32, uint32(48)},
|
|
||||||
{pb.E_DefaultFixed64, setUint64, uint64(49)},
|
|
||||||
{pb.E_DefaultSfixed32, setInt32, int32(50)},
|
|
||||||
{pb.E_DefaultSfixed64, setInt64, int64(51)},
|
|
||||||
{pb.E_DefaultBool, setBool, true},
|
|
||||||
{pb.E_DefaultBool, setBool2, true},
|
|
||||||
{pb.E_DefaultString, setString, "Hello, string"},
|
|
||||||
{pb.E_DefaultBytes, setBytes, []byte("Hello, bytes")},
|
|
||||||
{pb.E_DefaultEnum, setEnum, pb.DefaultsMessage_ONE},
|
|
||||||
}
|
|
||||||
|
|
||||||
checkVal := func(test testcase, msg *pb.DefaultsMessage, valWant interface{}) error {
|
|
||||||
val, err := proto.GetExtension(msg, test.ext)
|
|
||||||
if err != nil {
|
|
||||||
if valWant != nil {
|
|
||||||
return fmt.Errorf("GetExtension(): %s", err)
|
|
||||||
}
|
|
||||||
if want := proto.ErrMissingExtension; err != want {
|
|
||||||
return fmt.Errorf("Unexpected error: got %v, want %v", err, want)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// All proto2 extension values are either a pointer to a value or a slice of values.
|
|
||||||
ty := reflect.TypeOf(val)
|
|
||||||
tyWant := reflect.TypeOf(test.ext.ExtensionType)
|
|
||||||
if got, want := ty, tyWant; got != want {
|
|
||||||
return fmt.Errorf("unexpected reflect.TypeOf(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
tye := ty.Elem()
|
|
||||||
tyeWant := tyWant.Elem()
|
|
||||||
if got, want := tye, tyeWant; got != want {
|
|
||||||
return fmt.Errorf("unexpected reflect.TypeOf().Elem(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the name of the type of the value.
|
|
||||||
// If it is an enum it will be type int32 with the name of the enum.
|
|
||||||
if got, want := tye.Name(), tye.Name(); got != want {
|
|
||||||
return fmt.Errorf("unexpected reflect.TypeOf().Elem().Name(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that value is what we expect.
|
|
||||||
// If we have a pointer in val, get the value it points to.
|
|
||||||
valExp := val
|
|
||||||
if ty.Kind() == reflect.Ptr {
|
|
||||||
valExp = reflect.ValueOf(val).Elem().Interface()
|
|
||||||
}
|
|
||||||
if got, want := valExp, valWant; !reflect.DeepEqual(got, want) {
|
|
||||||
return fmt.Errorf("unexpected reflect.DeepEqual(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
setTo := func(test testcase) interface{} {
|
|
||||||
setTo := reflect.ValueOf(test.want)
|
|
||||||
if typ := reflect.TypeOf(test.ext.ExtensionType); typ.Kind() == reflect.Ptr {
|
|
||||||
setTo = reflect.New(typ).Elem()
|
|
||||||
setTo.Set(reflect.New(setTo.Type().Elem()))
|
|
||||||
setTo.Elem().Set(reflect.ValueOf(test.want))
|
|
||||||
}
|
|
||||||
return setTo.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
msg := &pb.DefaultsMessage{}
|
|
||||||
name := test.ext.Name
|
|
||||||
|
|
||||||
// Check the initial value.
|
|
||||||
if err := checkVal(test, msg, test.def); err != nil {
|
|
||||||
t.Errorf("%s: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the per-type value and check value.
|
|
||||||
name = fmt.Sprintf("%s (set to %T %v)", name, test.want, test.want)
|
|
||||||
if err := proto.SetExtension(msg, test.ext, setTo(test)); err != nil {
|
|
||||||
t.Errorf("%s: SetExtension(): %v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkVal(test, msg, test.want); err != nil {
|
|
||||||
t.Errorf("%s: %v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set and check the value.
|
|
||||||
name += " (cleared)"
|
|
||||||
proto.ClearExtension(msg, test.ext)
|
|
||||||
if err := checkVal(test, msg, test.def); err != nil {
|
|
||||||
t.Errorf("%s: %v", name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtensionsRoundTrip(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{}
|
|
||||||
ext1 := &pb.Ext{
|
|
||||||
Data: proto.String("hi"),
|
|
||||||
}
|
|
||||||
ext2 := &pb.Ext{
|
|
||||||
Data: proto.String("there"),
|
|
||||||
}
|
|
||||||
exists := proto.HasExtension(msg, pb.E_Ext_More)
|
|
||||||
if exists {
|
|
||||||
t.Error("Extension More present unexpectedly")
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
e, err := proto.GetExtension(msg, pb.E_Ext_More)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
x, ok := e.(*pb.Ext)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("e has type %T, expected testdata.Ext", e)
|
|
||||||
} else if *x.Data != "there" {
|
|
||||||
t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x)
|
|
||||||
}
|
|
||||||
proto.ClearExtension(msg, pb.E_Ext_More)
|
|
||||||
if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension {
|
|
||||||
t.Errorf("got %v, expected ErrMissingExtension", e)
|
|
||||||
}
|
|
||||||
if _, err := proto.GetExtension(msg, pb.E_X215); err == nil {
|
|
||||||
t.Error("expected bad extension error, got nil")
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil {
|
|
||||||
t.Error("expected extension err")
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil {
|
|
||||||
t.Error("expected some sort of type mismatch error, got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNilExtension(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{
|
|
||||||
Count: proto.Int32(1),
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_Text, proto.String("hello")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, (*pb.Ext)(nil)); err == nil {
|
|
||||||
t.Error("expected SetExtension to fail due to a nil extension")
|
|
||||||
} else if want := "proto: SetExtension called with nil value of type *testdata.Ext"; err.Error() != want {
|
|
||||||
t.Errorf("expected error %v, got %v", want, err)
|
|
||||||
}
|
|
||||||
// Note: if the behavior of Marshal is ever changed to ignore nil extensions, update
|
|
||||||
// this test to verify that E_Ext_Text is properly propagated through marshal->unmarshal.
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalUnmarshalRepeatedExtension(t *testing.T) {
|
|
||||||
// Add a repeated extension to the result.
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
ext []*pb.ComplexExtension
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"two fields",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{First: proto.Int32(7)},
|
|
||||||
{Second: proto.Int32(11)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two fields and repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{First: proto.Int32(9)},
|
|
||||||
{Second: proto.Int32(21)},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
// Marshal message with a repeated extension.
|
|
||||||
msg1 := new(pb.OtherMessage)
|
|
||||||
err := proto.SetExtension(msg1, pb.E_RComplex, test.ext)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error setting extension: %v", test.name, err)
|
|
||||||
}
|
|
||||||
b, err := proto.Marshal(msg1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error marshaling message: %v", test.name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal and read the merged proto.
|
|
||||||
msg2 := new(pb.OtherMessage)
|
|
||||||
err = proto.Unmarshal(b, msg2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error unmarshaling message: %v", test.name, err)
|
|
||||||
}
|
|
||||||
e, err := proto.GetExtension(msg2, pb.E_RComplex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error getting extension: %v", test.name, err)
|
|
||||||
}
|
|
||||||
ext := e.([]*pb.ComplexExtension)
|
|
||||||
if ext == nil {
|
|
||||||
t.Fatalf("[%s] Invalid extension", test.name)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(ext, test.ext) {
|
|
||||||
t.Errorf("[%s] Wrong value for ComplexExtension: got: %v want: %v\n", test.name, ext, test.ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalRepeatingNonRepeatedExtension(t *testing.T) {
|
|
||||||
// We may see multiple instances of the same extension in the wire
|
|
||||||
// format. For example, the proto compiler may encode custom options in
|
|
||||||
// this way. Here, we verify that we merge the extensions together.
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
ext []*pb.ComplexExtension
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"two fields",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{First: proto.Int32(7)},
|
|
||||||
{Second: proto.Int32(11)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two fields and repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{First: proto.Int32(9)},
|
|
||||||
{Second: proto.Int32(21)},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
var want pb.ComplexExtension
|
|
||||||
|
|
||||||
// Generate a serialized representation of a repeated extension
|
|
||||||
// by catenating bytes together.
|
|
||||||
for i, e := range test.ext {
|
|
||||||
// Merge to create the wanted proto.
|
|
||||||
proto.Merge(&want, e)
|
|
||||||
|
|
||||||
// serialize the message
|
|
||||||
msg := new(pb.OtherMessage)
|
|
||||||
err := proto.SetExtension(msg, pb.E_Complex, e)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error setting extension %d: %v", test.name, i, err)
|
|
||||||
}
|
|
||||||
b, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error marshaling message %d: %v", test.name, i, err)
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal and read the merged proto.
|
|
||||||
msg2 := new(pb.OtherMessage)
|
|
||||||
err := proto.Unmarshal(buf.Bytes(), msg2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error unmarshaling message: %v", test.name, err)
|
|
||||||
}
|
|
||||||
e, err := proto.GetExtension(msg2, pb.E_Complex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error getting extension: %v", test.name, err)
|
|
||||||
}
|
|
||||||
ext := e.(*pb.ComplexExtension)
|
|
||||||
if ext == nil {
|
|
||||||
t.Fatalf("[%s] Invalid extension", test.name)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(*ext, want) {
|
|
||||||
t.Errorf("[%s] Wrong value for ComplexExtension: got: %s want: %s\n", test.name, ext, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClearAllExtensions(t *testing.T) {
|
|
||||||
// unregistered extension
|
|
||||||
desc := &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*pb.MyMessage)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 101010100,
|
|
||||||
Name: "emptyextension",
|
|
||||||
Tag: "varint,0,opt",
|
|
||||||
}
|
|
||||||
m := &pb.MyMessage{}
|
|
||||||
if proto.HasExtension(m, desc) {
|
|
||||||
t.Errorf("proto.HasExtension(%s): got true, want false", proto.MarshalTextString(m))
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(m, desc, proto.Bool(true)); err != nil {
|
|
||||||
t.Errorf("proto.SetExtension(m, desc, true): got error %q, want nil", err)
|
|
||||||
}
|
|
||||||
if !proto.HasExtension(m, desc) {
|
|
||||||
t.Errorf("proto.HasExtension(%s): got false, want true", proto.MarshalTextString(m))
|
|
||||||
}
|
|
||||||
proto.ClearAllExtensions(m)
|
|
||||||
if proto.HasExtension(m, desc) {
|
|
||||||
t.Errorf("proto.HasExtension(%s): got true, want false", proto.MarshalTextString(m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalRace(t *testing.T) {
|
|
||||||
// unregistered extension
|
|
||||||
desc := &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*pb.MyMessage)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 101010100,
|
|
||||||
Name: "emptyextension",
|
|
||||||
Tag: "varint,0,opt",
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &pb.MyMessage{Count: proto.Int32(4)}
|
|
||||||
if err := proto.SetExtension(m, desc, proto.Bool(true)); err != nil {
|
|
||||||
t.Errorf("proto.SetExtension(m, desc, true): got error %q, want nil", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var g errgroup.Group
|
|
||||||
for n := 3; n > 0; n-- {
|
|
||||||
g.Go(func() error {
|
|
||||||
_, err := proto.Marshal(m)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := g.Wait(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
898
vendor/src/github.com/golang/protobuf/proto/lib.go
vendored
898
vendor/src/github.com/golang/protobuf/proto/lib.go
vendored
|
@ -1,898 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package proto converts data structures to and from the wire format of
|
|
||||||
protocol buffers. It works in concert with the Go source code generated
|
|
||||||
for .proto files by the protocol compiler.
|
|
||||||
|
|
||||||
A summary of the properties of the protocol buffer interface
|
|
||||||
for a protocol buffer variable v:
|
|
||||||
|
|
||||||
- Names are turned from camel_case to CamelCase for export.
|
|
||||||
- There are no methods on v to set fields; just treat
|
|
||||||
them as structure fields.
|
|
||||||
- There are getters that return a field's value if set,
|
|
||||||
and return the field's default value if unset.
|
|
||||||
The getters work even if the receiver is a nil message.
|
|
||||||
- The zero value for a struct is its correct initialization state.
|
|
||||||
All desired fields must be set before marshaling.
|
|
||||||
- A Reset() method will restore a protobuf struct to its zero state.
|
|
||||||
- Non-repeated fields are pointers to the values; nil means unset.
|
|
||||||
That is, optional or required field int32 f becomes F *int32.
|
|
||||||
- Repeated fields are slices.
|
|
||||||
- Helper functions are available to aid the setting of fields.
|
|
||||||
msg.Foo = proto.String("hello") // set field
|
|
||||||
- Constants are defined to hold the default values of all fields that
|
|
||||||
have them. They have the form Default_StructName_FieldName.
|
|
||||||
Because the getter methods handle defaulted values,
|
|
||||||
direct use of these constants should be rare.
|
|
||||||
- Enums are given type names and maps from names to values.
|
|
||||||
Enum values are prefixed by the enclosing message's name, or by the
|
|
||||||
enum's type name if it is a top-level enum. Enum types have a String
|
|
||||||
method, and a Enum method to assist in message construction.
|
|
||||||
- Nested messages, groups and enums have type names prefixed with the name of
|
|
||||||
the surrounding message type.
|
|
||||||
- Extensions are given descriptor names that start with E_,
|
|
||||||
followed by an underscore-delimited list of the nested messages
|
|
||||||
that contain it (if any) followed by the CamelCased name of the
|
|
||||||
extension field itself. HasExtension, ClearExtension, GetExtension
|
|
||||||
and SetExtension are functions for manipulating extensions.
|
|
||||||
- Oneof field sets are given a single field in their message,
|
|
||||||
with distinguished wrapper types for each possible field value.
|
|
||||||
- Marshal and Unmarshal are functions to encode and decode the wire format.
|
|
||||||
|
|
||||||
When the .proto file specifies `syntax="proto3"`, there are some differences:
|
|
||||||
|
|
||||||
- Non-repeated fields of non-message type are values instead of pointers.
|
|
||||||
- Getters are only generated for message and oneof fields.
|
|
||||||
- Enum types do not get an Enum method.
|
|
||||||
|
|
||||||
The simplest way to describe this is to see an example.
|
|
||||||
Given file test.proto, containing
|
|
||||||
|
|
||||||
package example;
|
|
||||||
|
|
||||||
enum FOO { X = 17; }
|
|
||||||
|
|
||||||
message Test {
|
|
||||||
required string label = 1;
|
|
||||||
optional int32 type = 2 [default=77];
|
|
||||||
repeated int64 reps = 3;
|
|
||||||
optional group OptionalGroup = 4 {
|
|
||||||
required string RequiredField = 5;
|
|
||||||
}
|
|
||||||
oneof union {
|
|
||||||
int32 number = 6;
|
|
||||||
string name = 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The resulting file, test.pb.go, is:
|
|
||||||
|
|
||||||
package example
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
type FOO int32
|
|
||||||
const (
|
|
||||||
FOO_X FOO = 17
|
|
||||||
)
|
|
||||||
var FOO_name = map[int32]string{
|
|
||||||
17: "X",
|
|
||||||
}
|
|
||||||
var FOO_value = map[string]int32{
|
|
||||||
"X": 17,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x FOO) Enum() *FOO {
|
|
||||||
p := new(FOO)
|
|
||||||
*p = x
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
func (x FOO) String() string {
|
|
||||||
return proto.EnumName(FOO_name, int32(x))
|
|
||||||
}
|
|
||||||
func (x *FOO) UnmarshalJSON(data []byte) error {
|
|
||||||
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*x = FOO(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test struct {
|
|
||||||
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
|
|
||||||
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
|
|
||||||
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
|
|
||||||
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
|
|
||||||
// Types that are valid to be assigned to Union:
|
|
||||||
// *Test_Number
|
|
||||||
// *Test_Name
|
|
||||||
Union isTest_Union `protobuf_oneof:"union"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
func (m *Test) Reset() { *m = Test{} }
|
|
||||||
func (m *Test) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Test) ProtoMessage() {}
|
|
||||||
|
|
||||||
type isTest_Union interface {
|
|
||||||
isTest_Union()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test_Number struct {
|
|
||||||
Number int32 `protobuf:"varint,6,opt,name=number"`
|
|
||||||
}
|
|
||||||
type Test_Name struct {
|
|
||||||
Name string `protobuf:"bytes,7,opt,name=name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Test_Number) isTest_Union() {}
|
|
||||||
func (*Test_Name) isTest_Union() {}
|
|
||||||
|
|
||||||
func (m *Test) GetUnion() isTest_Union {
|
|
||||||
if m != nil {
|
|
||||||
return m.Union
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
const Default_Test_Type int32 = 77
|
|
||||||
|
|
||||||
func (m *Test) GetLabel() string {
|
|
||||||
if m != nil && m.Label != nil {
|
|
||||||
return *m.Label
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetType() int32 {
|
|
||||||
if m != nil && m.Type != nil {
|
|
||||||
return *m.Type
|
|
||||||
}
|
|
||||||
return Default_Test_Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
|
|
||||||
if m != nil {
|
|
||||||
return m.Optionalgroup
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Test_OptionalGroup struct {
|
|
||||||
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
|
|
||||||
}
|
|
||||||
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
|
|
||||||
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
|
|
||||||
|
|
||||||
func (m *Test_OptionalGroup) GetRequiredField() string {
|
|
||||||
if m != nil && m.RequiredField != nil {
|
|
||||||
return *m.RequiredField
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetNumber() int32 {
|
|
||||||
if x, ok := m.GetUnion().(*Test_Number); ok {
|
|
||||||
return x.Number
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Test) GetName() string {
|
|
||||||
if x, ok := m.GetUnion().(*Test_Name); ok {
|
|
||||||
return x.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
To create and play with a Test object:
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
pb "./example.pb"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
test := &pb.Test{
|
|
||||||
Label: proto.String("hello"),
|
|
||||||
Type: proto.Int32(17),
|
|
||||||
Reps: []int64{1, 2, 3},
|
|
||||||
Optionalgroup: &pb.Test_OptionalGroup{
|
|
||||||
RequiredField: proto.String("good bye"),
|
|
||||||
},
|
|
||||||
Union: &pb.Test_Name{"fred"},
|
|
||||||
}
|
|
||||||
data, err := proto.Marshal(test)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("marshaling error: ", err)
|
|
||||||
}
|
|
||||||
newTest := &pb.Test{}
|
|
||||||
err = proto.Unmarshal(data, newTest)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("unmarshaling error: ", err)
|
|
||||||
}
|
|
||||||
// Now test and newTest contain the same data.
|
|
||||||
if test.GetLabel() != newTest.GetLabel() {
|
|
||||||
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
|
|
||||||
}
|
|
||||||
// Use a type switch to determine which oneof was set.
|
|
||||||
switch u := test.Union.(type) {
|
|
||||||
case *pb.Test_Number: // u.Number contains the number.
|
|
||||||
case *pb.Test_Name: // u.Name contains the string.
|
|
||||||
}
|
|
||||||
// etc.
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Message is implemented by generated protocol buffer messages.
|
|
||||||
type Message interface {
|
|
||||||
Reset()
|
|
||||||
String() string
|
|
||||||
ProtoMessage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stats records allocation details about the protocol buffer encoders
|
|
||||||
// and decoders. Useful for tuning the library itself.
|
|
||||||
type Stats struct {
|
|
||||||
Emalloc uint64 // mallocs in encode
|
|
||||||
Dmalloc uint64 // mallocs in decode
|
|
||||||
Encode uint64 // number of encodes
|
|
||||||
Decode uint64 // number of decodes
|
|
||||||
Chit uint64 // number of cache hits
|
|
||||||
Cmiss uint64 // number of cache misses
|
|
||||||
Size uint64 // number of sizes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set to true to enable stats collection.
|
|
||||||
const collectStats = false
|
|
||||||
|
|
||||||
var stats Stats
|
|
||||||
|
|
||||||
// GetStats returns a copy of the global Stats structure.
|
|
||||||
func GetStats() Stats { return stats }
|
|
||||||
|
|
||||||
// A Buffer is a buffer manager for marshaling and unmarshaling
|
|
||||||
// protocol buffers. It may be reused between invocations to
|
|
||||||
// reduce memory usage. It is not necessary to use a Buffer;
|
|
||||||
// the global functions Marshal and Unmarshal create a
|
|
||||||
// temporary Buffer and are fine for most applications.
|
|
||||||
type Buffer struct {
|
|
||||||
buf []byte // encode/decode byte stream
|
|
||||||
index int // read point
|
|
||||||
|
|
||||||
// pools of basic types to amortize allocation.
|
|
||||||
bools []bool
|
|
||||||
uint32s []uint32
|
|
||||||
uint64s []uint64
|
|
||||||
|
|
||||||
// extra pools, only used with pointer_reflect.go
|
|
||||||
int32s []int32
|
|
||||||
int64s []int64
|
|
||||||
float32s []float32
|
|
||||||
float64s []float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuffer allocates a new Buffer and initializes its internal data to
|
|
||||||
// the contents of the argument slice.
|
|
||||||
func NewBuffer(e []byte) *Buffer {
|
|
||||||
return &Buffer{buf: e}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset resets the Buffer, ready for marshaling a new protocol buffer.
|
|
||||||
func (p *Buffer) Reset() {
|
|
||||||
p.buf = p.buf[0:0] // for reading/writing
|
|
||||||
p.index = 0 // for reading
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBuf replaces the internal buffer with the slice,
|
|
||||||
// ready for unmarshaling the contents of the slice.
|
|
||||||
func (p *Buffer) SetBuf(s []byte) {
|
|
||||||
p.buf = s
|
|
||||||
p.index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the contents of the Buffer.
|
|
||||||
func (p *Buffer) Bytes() []byte { return p.buf }
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int is a helper routine that allocates a new int32 value
|
|
||||||
// to store v and returns a pointer to it, but unlike Int32
|
|
||||||
// its argument value is an int.
|
|
||||||
func Int(v int) *int32 {
|
|
||||||
p := new(int32)
|
|
||||||
*p = int32(v)
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float32 is a helper routine that allocates a new float32 value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Float32(v float32) *float32 {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumName is a helper function to simplify printing protocol buffer enums
|
|
||||||
// by name. Given an enum map and a value, it returns a useful string.
|
|
||||||
func EnumName(m map[int32]string, v int32) string {
|
|
||||||
s, ok := m[v]
|
|
||||||
if ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
|
|
||||||
// from their JSON-encoded representation. Given a map from the enum's symbolic
|
|
||||||
// names to its int values, and a byte buffer containing the JSON-encoded
|
|
||||||
// value, it returns an int32 that can be cast to the enum type by the caller.
|
|
||||||
//
|
|
||||||
// The function can deal with both JSON representations, numeric and symbolic.
|
|
||||||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
|
||||||
if data[0] == '"' {
|
|
||||||
// New style: enums are strings.
|
|
||||||
var repr string
|
|
||||||
if err := json.Unmarshal(data, &repr); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
val, ok := m[repr]
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
// Old style: enums are ints.
|
|
||||||
var val int32
|
|
||||||
if err := json.Unmarshal(data, &val); err != nil {
|
|
||||||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugPrint dumps the encoded data in b in a debugging format with a header
|
|
||||||
// including the string s. Used in testing but made available for general debugging.
|
|
||||||
func (p *Buffer) DebugPrint(s string, b []byte) {
|
|
||||||
var u uint64
|
|
||||||
|
|
||||||
obuf := p.buf
|
|
||||||
index := p.index
|
|
||||||
p.buf = b
|
|
||||||
p.index = 0
|
|
||||||
depth := 0
|
|
||||||
|
|
||||||
fmt.Printf("\n--- %s ---\n", s)
|
|
||||||
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
for i := 0; i < depth; i++ {
|
|
||||||
fmt.Print(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
index := p.index
|
|
||||||
if index == len(p.buf) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
op, err := p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: fetching op err %v\n", index, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
tag := op >> 3
|
|
||||||
wire := op & 7
|
|
||||||
|
|
||||||
switch wire {
|
|
||||||
default:
|
|
||||||
fmt.Printf("%3d: t=%3d unknown wire=%d\n",
|
|
||||||
index, tag, wire)
|
|
||||||
break out
|
|
||||||
|
|
||||||
case WireBytes:
|
|
||||||
var r []byte
|
|
||||||
|
|
||||||
r, err = p.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
|
|
||||||
if len(r) <= 6 {
|
|
||||||
for i := 0; i < len(r); i++ {
|
|
||||||
fmt.Printf(" %.2x", r[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
fmt.Printf(" %.2x", r[i])
|
|
||||||
}
|
|
||||||
fmt.Printf(" ..")
|
|
||||||
for i := len(r) - 3; i < len(r); i++ {
|
|
||||||
fmt.Printf(" %.2x", r[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
case WireFixed32:
|
|
||||||
u, err = p.DecodeFixed32()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
|
|
||||||
|
|
||||||
case WireFixed64:
|
|
||||||
u, err = p.DecodeFixed64()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
|
|
||||||
|
|
||||||
case WireVarint:
|
|
||||||
u, err = p.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
|
|
||||||
|
|
||||||
case WireStartGroup:
|
|
||||||
fmt.Printf("%3d: t=%3d start\n", index, tag)
|
|
||||||
depth++
|
|
||||||
|
|
||||||
case WireEndGroup:
|
|
||||||
depth--
|
|
||||||
fmt.Printf("%3d: t=%3d end\n", index, tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if depth != 0 {
|
|
||||||
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
p.buf = obuf
|
|
||||||
p.index = index
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaults sets unset protocol buffer fields to their default values.
|
|
||||||
// It only modifies fields that are both unset and have defined defaults.
|
|
||||||
// It recursively sets default values in any non-nil sub-messages.
|
|
||||||
func SetDefaults(pb Message) {
|
|
||||||
setDefaults(reflect.ValueOf(pb), true, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// v is a pointer to a struct.
|
|
||||||
func setDefaults(v reflect.Value, recur, zeros bool) {
|
|
||||||
v = v.Elem()
|
|
||||||
|
|
||||||
defaultMu.RLock()
|
|
||||||
dm, ok := defaults[v.Type()]
|
|
||||||
defaultMu.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
dm = buildDefaultMessage(v.Type())
|
|
||||||
defaultMu.Lock()
|
|
||||||
defaults[v.Type()] = dm
|
|
||||||
defaultMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sf := range dm.scalars {
|
|
||||||
f := v.Field(sf.index)
|
|
||||||
if !f.IsNil() {
|
|
||||||
// field already set
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dv := sf.value
|
|
||||||
if dv == nil && !zeros {
|
|
||||||
// no explicit default, and don't want to set zeros
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fptr := f.Addr().Interface() // **T
|
|
||||||
// TODO: Consider batching the allocations we do here.
|
|
||||||
switch sf.kind {
|
|
||||||
case reflect.Bool:
|
|
||||||
b := new(bool)
|
|
||||||
if dv != nil {
|
|
||||||
*b = dv.(bool)
|
|
||||||
}
|
|
||||||
*(fptr.(**bool)) = b
|
|
||||||
case reflect.Float32:
|
|
||||||
f := new(float32)
|
|
||||||
if dv != nil {
|
|
||||||
*f = dv.(float32)
|
|
||||||
}
|
|
||||||
*(fptr.(**float32)) = f
|
|
||||||
case reflect.Float64:
|
|
||||||
f := new(float64)
|
|
||||||
if dv != nil {
|
|
||||||
*f = dv.(float64)
|
|
||||||
}
|
|
||||||
*(fptr.(**float64)) = f
|
|
||||||
case reflect.Int32:
|
|
||||||
// might be an enum
|
|
||||||
if ft := f.Type(); ft != int32PtrType {
|
|
||||||
// enum
|
|
||||||
f.Set(reflect.New(ft.Elem()))
|
|
||||||
if dv != nil {
|
|
||||||
f.Elem().SetInt(int64(dv.(int32)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// int32 field
|
|
||||||
i := new(int32)
|
|
||||||
if dv != nil {
|
|
||||||
*i = dv.(int32)
|
|
||||||
}
|
|
||||||
*(fptr.(**int32)) = i
|
|
||||||
}
|
|
||||||
case reflect.Int64:
|
|
||||||
i := new(int64)
|
|
||||||
if dv != nil {
|
|
||||||
*i = dv.(int64)
|
|
||||||
}
|
|
||||||
*(fptr.(**int64)) = i
|
|
||||||
case reflect.String:
|
|
||||||
s := new(string)
|
|
||||||
if dv != nil {
|
|
||||||
*s = dv.(string)
|
|
||||||
}
|
|
||||||
*(fptr.(**string)) = s
|
|
||||||
case reflect.Uint8:
|
|
||||||
// exceptional case: []byte
|
|
||||||
var b []byte
|
|
||||||
if dv != nil {
|
|
||||||
db := dv.([]byte)
|
|
||||||
b = make([]byte, len(db))
|
|
||||||
copy(b, db)
|
|
||||||
} else {
|
|
||||||
b = []byte{}
|
|
||||||
}
|
|
||||||
*(fptr.(*[]byte)) = b
|
|
||||||
case reflect.Uint32:
|
|
||||||
u := new(uint32)
|
|
||||||
if dv != nil {
|
|
||||||
*u = dv.(uint32)
|
|
||||||
}
|
|
||||||
*(fptr.(**uint32)) = u
|
|
||||||
case reflect.Uint64:
|
|
||||||
u := new(uint64)
|
|
||||||
if dv != nil {
|
|
||||||
*u = dv.(uint64)
|
|
||||||
}
|
|
||||||
*(fptr.(**uint64)) = u
|
|
||||||
default:
|
|
||||||
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ni := range dm.nested {
|
|
||||||
f := v.Field(ni)
|
|
||||||
// f is *T or []*T or map[T]*T
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if f.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setDefaults(f, recur, zeros)
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
for i := 0; i < f.Len(); i++ {
|
|
||||||
e := f.Index(i)
|
|
||||||
if e.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setDefaults(e, recur, zeros)
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
for _, k := range f.MapKeys() {
|
|
||||||
e := f.MapIndex(k)
|
|
||||||
if e.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setDefaults(e, recur, zeros)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// defaults maps a protocol buffer struct type to a slice of the fields,
|
|
||||||
// with its scalar fields set to their proto-declared non-zero default values.
|
|
||||||
defaultMu sync.RWMutex
|
|
||||||
defaults = make(map[reflect.Type]defaultMessage)
|
|
||||||
|
|
||||||
int32PtrType = reflect.TypeOf((*int32)(nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultMessage represents information about the default values of a message.
|
|
||||||
type defaultMessage struct {
|
|
||||||
scalars []scalarField
|
|
||||||
nested []int // struct field index of nested messages
|
|
||||||
}
|
|
||||||
|
|
||||||
type scalarField struct {
|
|
||||||
index int // struct field index
|
|
||||||
kind reflect.Kind // element type (the T in *T or []T)
|
|
||||||
value interface{} // the proto-declared default value, or nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// t is a struct type.
|
|
||||||
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
|
|
||||||
sprop := GetProperties(t)
|
|
||||||
for _, prop := range sprop.Prop {
|
|
||||||
fi, ok := sprop.decoderTags.get(prop.Tag)
|
|
||||||
if !ok {
|
|
||||||
// XXX_unrecognized
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ft := t.Field(fi).Type
|
|
||||||
|
|
||||||
sf, nested, err := fieldDefault(ft, prop)
|
|
||||||
switch {
|
|
||||||
case err != nil:
|
|
||||||
log.Print(err)
|
|
||||||
case nested:
|
|
||||||
dm.nested = append(dm.nested, fi)
|
|
||||||
case sf != nil:
|
|
||||||
sf.index = fi
|
|
||||||
dm.scalars = append(dm.scalars, *sf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dm
|
|
||||||
}
|
|
||||||
|
|
||||||
// fieldDefault returns the scalarField for field type ft.
|
|
||||||
// sf will be nil if the field can not have a default.
|
|
||||||
// nestedMessage will be true if this is a nested message.
|
|
||||||
// Note that sf.index is not set on return.
|
|
||||||
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
|
|
||||||
var canHaveDefault bool
|
|
||||||
switch ft.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
if ft.Elem().Kind() == reflect.Struct {
|
|
||||||
nestedMessage = true
|
|
||||||
} else {
|
|
||||||
canHaveDefault = true // proto2 scalar field
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
switch ft.Elem().Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
nestedMessage = true // repeated message
|
|
||||||
case reflect.Uint8:
|
|
||||||
canHaveDefault = true // bytes field
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
if ft.Elem().Kind() == reflect.Ptr {
|
|
||||||
nestedMessage = true // map with message values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !canHaveDefault {
|
|
||||||
if nestedMessage {
|
|
||||||
return nil, true, nil
|
|
||||||
}
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now know that ft is a pointer or slice.
|
|
||||||
sf = &scalarField{kind: ft.Elem().Kind()}
|
|
||||||
|
|
||||||
// scalar fields without defaults
|
|
||||||
if !prop.HasDefault {
|
|
||||||
return sf, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// a scalar field: either *T or []byte
|
|
||||||
switch ft.Elem().Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
x, err := strconv.ParseBool(prop.Default)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
case reflect.Float32:
|
|
||||||
x, err := strconv.ParseFloat(prop.Default, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = float32(x)
|
|
||||||
case reflect.Float64:
|
|
||||||
x, err := strconv.ParseFloat(prop.Default, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
case reflect.Int32:
|
|
||||||
x, err := strconv.ParseInt(prop.Default, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = int32(x)
|
|
||||||
case reflect.Int64:
|
|
||||||
x, err := strconv.ParseInt(prop.Default, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
case reflect.String:
|
|
||||||
sf.value = prop.Default
|
|
||||||
case reflect.Uint8:
|
|
||||||
// []byte (not *uint8)
|
|
||||||
sf.value = []byte(prop.Default)
|
|
||||||
case reflect.Uint32:
|
|
||||||
x, err := strconv.ParseUint(prop.Default, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = uint32(x)
|
|
||||||
case reflect.Uint64:
|
|
||||||
x, err := strconv.ParseUint(prop.Default, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
|
|
||||||
}
|
|
||||||
sf.value = x
|
|
||||||
default:
|
|
||||||
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return sf, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map fields may have key types of non-float scalars, strings and enums.
|
|
||||||
// The easiest way to sort them in some deterministic order is to use fmt.
|
|
||||||
// If this turns out to be inefficient we can always consider other options,
|
|
||||||
// such as doing a Schwartzian transform.
|
|
||||||
|
|
||||||
func mapKeys(vs []reflect.Value) sort.Interface {
|
|
||||||
s := mapKeySorter{
|
|
||||||
vs: vs,
|
|
||||||
// default Less function: textual comparison
|
|
||||||
less: func(a, b reflect.Value) bool {
|
|
||||||
return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
|
|
||||||
// numeric keys are sorted numerically.
|
|
||||||
if len(vs) == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
switch vs[0].Kind() {
|
|
||||||
case reflect.Int32, reflect.Int64:
|
|
||||||
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
|
|
||||||
case reflect.Uint32, reflect.Uint64:
|
|
||||||
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
type mapKeySorter struct {
|
|
||||||
vs []reflect.Value
|
|
||||||
less func(a, b reflect.Value) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s mapKeySorter) Len() int { return len(s.vs) }
|
|
||||||
func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
|
|
||||||
func (s mapKeySorter) Less(i, j int) bool {
|
|
||||||
return s.less(s.vs[i], s.vs[j])
|
|
||||||
}
|
|
||||||
|
|
||||||
// isProto3Zero reports whether v is a zero proto3 value.
|
|
||||||
func isProto3Zero(v reflect.Value) bool {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return !v.Bool()
|
|
||||||
case reflect.Int32, reflect.Int64:
|
|
||||||
return v.Int() == 0
|
|
||||||
case reflect.Uint32, reflect.Uint64:
|
|
||||||
return v.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return v.Float() == 0
|
|
||||||
case reflect.String:
|
|
||||||
return v.String() == ""
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
|
||||||
const ProtoPackageIsVersion2 = true
|
|
||||||
|
|
||||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
|
||||||
// to assert that that code is compatible with this version of the proto package.
|
|
||||||
const ProtoPackageIsVersion1 = true
|
|
|
@ -1,46 +0,0 @@
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
ppb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func marshalled() []byte {
|
|
||||||
m := &ppb.IntMaps{}
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
m.Maps = append(m.Maps, &ppb.IntMap{
|
|
||||||
Rtt: map[int32]int32{1: 2},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
b, err := proto.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Can't marshal %+v: %v", m, err))
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkConcurrentMapUnmarshal(b *testing.B) {
|
|
||||||
in := marshalled()
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
var out ppb.IntMaps
|
|
||||||
if err := proto.Unmarshal(in, &out); err != nil {
|
|
||||||
b.Errorf("Can't unmarshal ppb.IntMaps: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSequentialMapUnmarshal(b *testing.B) {
|
|
||||||
in := marshalled()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
var out ppb.IntMaps
|
|
||||||
if err := proto.Unmarshal(in, &out); err != nil {
|
|
||||||
b.Errorf("Can't unmarshal ppb.IntMaps: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,311 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Support for message sets.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
|
||||||
// A message type ID is required for storing a protocol buffer in a message set.
|
|
||||||
var errNoMessageTypeID = errors.New("proto does not have a message type ID")
|
|
||||||
|
|
||||||
// The first two types (_MessageSet_Item and messageSet)
|
|
||||||
// model what the protocol compiler produces for the following protocol message:
|
|
||||||
// message MessageSet {
|
|
||||||
// repeated group Item = 1 {
|
|
||||||
// required int32 type_id = 2;
|
|
||||||
// required string message = 3;
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// That is the MessageSet wire format. We can't use a proto to generate these
|
|
||||||
// because that would introduce a circular dependency between it and this package.
|
|
||||||
|
|
||||||
type _MessageSet_Item struct {
|
|
||||||
TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
|
|
||||||
Message []byte `protobuf:"bytes,3,req,name=message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageSet struct {
|
|
||||||
Item []*_MessageSet_Item `protobuf:"group,1,rep"`
|
|
||||||
XXX_unrecognized []byte
|
|
||||||
// TODO: caching?
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure messageSet is a Message.
|
|
||||||
var _ Message = (*messageSet)(nil)
|
|
||||||
|
|
||||||
// messageTypeIder is an interface satisfied by a protocol buffer type
|
|
||||||
// that may be stored in a MessageSet.
|
|
||||||
type messageTypeIder interface {
|
|
||||||
MessageTypeId() int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) find(pb Message) *_MessageSet_Item {
|
|
||||||
mti, ok := pb.(messageTypeIder)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
id := mti.MessageTypeId()
|
|
||||||
for _, item := range ms.Item {
|
|
||||||
if *item.TypeId == id {
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Has(pb Message) bool {
|
|
||||||
if ms.find(pb) != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Unmarshal(pb Message) error {
|
|
||||||
if item := ms.find(pb); item != nil {
|
|
||||||
return Unmarshal(item.Message, pb)
|
|
||||||
}
|
|
||||||
if _, ok := pb.(messageTypeIder); !ok {
|
|
||||||
return errNoMessageTypeID
|
|
||||||
}
|
|
||||||
return nil // TODO: return error instead?
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Marshal(pb Message) error {
|
|
||||||
msg, err := Marshal(pb)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if item := ms.find(pb); item != nil {
|
|
||||||
// reuse existing item
|
|
||||||
item.Message = msg
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mti, ok := pb.(messageTypeIder)
|
|
||||||
if !ok {
|
|
||||||
return errNoMessageTypeID
|
|
||||||
}
|
|
||||||
|
|
||||||
mtid := mti.MessageTypeId()
|
|
||||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
|
||||||
TypeId: &mtid,
|
|
||||||
Message: msg,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *messageSet) Reset() { *ms = messageSet{} }
|
|
||||||
func (ms *messageSet) String() string { return CompactTextString(ms) }
|
|
||||||
func (*messageSet) ProtoMessage() {}
|
|
||||||
|
|
||||||
// Support for the message_set_wire_format message option.
|
|
||||||
|
|
||||||
func skipVarint(buf []byte) []byte {
|
|
||||||
i := 0
|
|
||||||
for ; buf[i]&0x80 != 0; i++ {
|
|
||||||
}
|
|
||||||
return buf[i+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
|
||||||
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func MarshalMessageSet(exts interface{}) ([]byte, error) {
|
|
||||||
var m map[int32]Extension
|
|
||||||
switch exts := exts.(type) {
|
|
||||||
case *XXX_InternalExtensions:
|
|
||||||
if err := encodeExtensions(exts); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m, _ = exts.extensionsRead()
|
|
||||||
case map[int32]Extension:
|
|
||||||
if err := encodeExtensionsMap(exts); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m = exts
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proto: not an extension map")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort extension IDs to provide a deterministic encoding.
|
|
||||||
// See also enc_map in encode.go.
|
|
||||||
ids := make([]int, 0, len(m))
|
|
||||||
for id := range m {
|
|
||||||
ids = append(ids, int(id))
|
|
||||||
}
|
|
||||||
sort.Ints(ids)
|
|
||||||
|
|
||||||
ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))}
|
|
||||||
for _, id := range ids {
|
|
||||||
e := m[int32(id)]
|
|
||||||
// Remove the wire type and field number varint, as well as the length varint.
|
|
||||||
msg := skipVarint(skipVarint(e.enc))
|
|
||||||
|
|
||||||
ms.Item = append(ms.Item, &_MessageSet_Item{
|
|
||||||
TypeId: Int32(int32(id)),
|
|
||||||
Message: msg,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return Marshal(ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
|
||||||
// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
|
||||||
var m map[int32]Extension
|
|
||||||
switch exts := exts.(type) {
|
|
||||||
case *XXX_InternalExtensions:
|
|
||||||
m = exts.extensionsWrite()
|
|
||||||
case map[int32]Extension:
|
|
||||||
m = exts
|
|
||||||
default:
|
|
||||||
return errors.New("proto: not an extension map")
|
|
||||||
}
|
|
||||||
|
|
||||||
ms := new(messageSet)
|
|
||||||
if err := Unmarshal(buf, ms); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, item := range ms.Item {
|
|
||||||
id := *item.TypeId
|
|
||||||
msg := item.Message
|
|
||||||
|
|
||||||
// Restore wire type and field number varint, plus length varint.
|
|
||||||
// Be careful to preserve duplicate items.
|
|
||||||
b := EncodeVarint(uint64(id)<<3 | WireBytes)
|
|
||||||
if ext, ok := m[id]; ok {
|
|
||||||
// Existing data; rip off the tag and length varint
|
|
||||||
// so we join the new data correctly.
|
|
||||||
// We can assume that ext.enc is set because we are unmarshaling.
|
|
||||||
o := ext.enc[len(b):] // skip wire type and field number
|
|
||||||
_, n := DecodeVarint(o) // calculate length of length varint
|
|
||||||
o = o[n:] // skip length varint
|
|
||||||
msg = append(o, msg...) // join old data and new data
|
|
||||||
}
|
|
||||||
b = append(b, EncodeVarint(uint64(len(msg)))...)
|
|
||||||
b = append(b, msg...)
|
|
||||||
|
|
||||||
m[id] = Extension{enc: b}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
|
|
||||||
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
|
|
||||||
var m map[int32]Extension
|
|
||||||
switch exts := exts.(type) {
|
|
||||||
case *XXX_InternalExtensions:
|
|
||||||
m, _ = exts.extensionsRead()
|
|
||||||
case map[int32]Extension:
|
|
||||||
m = exts
|
|
||||||
default:
|
|
||||||
return nil, errors.New("proto: not an extension map")
|
|
||||||
}
|
|
||||||
var b bytes.Buffer
|
|
||||||
b.WriteByte('{')
|
|
||||||
|
|
||||||
// Process the map in key order for deterministic output.
|
|
||||||
ids := make([]int32, 0, len(m))
|
|
||||||
for id := range m {
|
|
||||||
ids = append(ids, id)
|
|
||||||
}
|
|
||||||
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
|
|
||||||
|
|
||||||
for i, id := range ids {
|
|
||||||
ext := m[id]
|
|
||||||
if i > 0 {
|
|
||||||
b.WriteByte(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
msd, ok := messageSetMap[id]
|
|
||||||
if !ok {
|
|
||||||
// Unknown type; we can't render it, so skip it.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
|
||||||
|
|
||||||
x := ext.value
|
|
||||||
if x == nil {
|
|
||||||
x = reflect.New(msd.t.Elem()).Interface()
|
|
||||||
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d, err := json.Marshal(x)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b.Write(d)
|
|
||||||
}
|
|
||||||
b.WriteByte('}')
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
|
|
||||||
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
|
||||||
func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
|
|
||||||
// Common-case fast path.
|
|
||||||
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is fairly tricky, and it's not clear that it is needed.
|
|
||||||
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// A global registry of types that can be used in a MessageSet.
|
|
||||||
|
|
||||||
var messageSetMap = make(map[int32]messageSetDesc)
|
|
||||||
|
|
||||||
type messageSetDesc struct {
|
|
||||||
t reflect.Type // pointer to struct
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterMessageSetType is called from the generated code.
|
|
||||||
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
|
|
||||||
messageSetMap[fieldNum] = messageSetDesc{
|
|
||||||
t: reflect.TypeOf(m),
|
|
||||||
name: name,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUnmarshalMessageSetWithDuplicate(t *testing.T) {
|
|
||||||
// Check that a repeated message set entry will be concatenated.
|
|
||||||
in := &messageSet{
|
|
||||||
Item: []*_MessageSet_Item{
|
|
||||||
{TypeId: Int32(12345), Message: []byte("hoo")},
|
|
||||||
{TypeId: Int32(12345), Message: []byte("hah")},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b, err := Marshal(in)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Marshal: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("Marshaled bytes: %q", b)
|
|
||||||
|
|
||||||
var extensions XXX_InternalExtensions
|
|
||||||
if err := UnmarshalMessageSet(b, &extensions); err != nil {
|
|
||||||
t.Fatalf("UnmarshalMessageSet: %v", err)
|
|
||||||
}
|
|
||||||
ext, ok := extensions.p.extensionMap[12345]
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Didn't retrieve extension 12345; map is %v", extensions.p.extensionMap)
|
|
||||||
}
|
|
||||||
// Skip wire type/field number and length varints.
|
|
||||||
got := skipVarint(skipVarint(ext.enc))
|
|
||||||
if want := []byte("hoohah"); !bytes.Equal(got, want) {
|
|
||||||
t.Errorf("Combined extension is %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,484 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// +build appengine js
|
|
||||||
|
|
||||||
// This file contains an implementation of proto field accesses using package reflect.
|
|
||||||
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
|
|
||||||
// be used on App Engine.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A structPointer is a pointer to a struct.
|
|
||||||
type structPointer struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
|
||||||
// The reflect value must itself be a pointer to a struct.
|
|
||||||
func toStructPointer(v reflect.Value) structPointer {
|
|
||||||
return structPointer{v}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil reports whether p is nil.
|
|
||||||
func structPointer_IsNil(p structPointer) bool {
|
|
||||||
return p.v.IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface returns the struct pointer as an interface value.
|
|
||||||
func structPointer_Interface(p structPointer, _ reflect.Type) interface{} {
|
|
||||||
return p.v.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A field identifies a field in a struct, accessible from a structPointer.
|
|
||||||
// In this implementation, a field is identified by the sequence of field indices
|
|
||||||
// passed to reflect's FieldByIndex.
|
|
||||||
type field []int
|
|
||||||
|
|
||||||
// toField returns a field equivalent to the given reflect field.
|
|
||||||
func toField(f *reflect.StructField) field {
|
|
||||||
return f.Index
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidField is an invalid field identifier.
|
|
||||||
var invalidField = field(nil)
|
|
||||||
|
|
||||||
// IsValid reports whether the field identifier is valid.
|
|
||||||
func (f field) IsValid() bool { return f != nil }
|
|
||||||
|
|
||||||
// field returns the given field in the struct as a reflect value.
|
|
||||||
func structPointer_field(p structPointer, f field) reflect.Value {
|
|
||||||
// Special case: an extension map entry with a value of type T
|
|
||||||
// passes a *T to the struct-handling code with a zero field,
|
|
||||||
// expecting that it will be treated as equivalent to *struct{ X T },
|
|
||||||
// which has the same memory layout. We have to handle that case
|
|
||||||
// specially, because reflect will panic if we call FieldByIndex on a
|
|
||||||
// non-struct.
|
|
||||||
if f == nil {
|
|
||||||
return p.v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.v.Elem().FieldByIndex(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ifield returns the given field in the struct as an interface value.
|
|
||||||
func structPointer_ifield(p structPointer, f field) interface{} {
|
|
||||||
return structPointer_field(p, f).Addr().Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the address of a []byte field in the struct.
|
|
||||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
|
||||||
return structPointer_ifield(p, f).(*[]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
|
||||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
|
||||||
return structPointer_ifield(p, f).(*[][]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool returns the address of a *bool field in the struct.
|
|
||||||
func structPointer_Bool(p structPointer, f field) **bool {
|
|
||||||
return structPointer_ifield(p, f).(**bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolVal returns the address of a bool field in the struct.
|
|
||||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
|
||||||
return structPointer_ifield(p, f).(*bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolSlice returns the address of a []bool field in the struct.
|
|
||||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
|
||||||
return structPointer_ifield(p, f).(*[]bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the address of a *string field in the struct.
|
|
||||||
func structPointer_String(p structPointer, f field) **string {
|
|
||||||
return structPointer_ifield(p, f).(**string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringVal returns the address of a string field in the struct.
|
|
||||||
func structPointer_StringVal(p structPointer, f field) *string {
|
|
||||||
return structPointer_ifield(p, f).(*string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice returns the address of a []string field in the struct.
|
|
||||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
|
||||||
return structPointer_ifield(p, f).(*[]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extensions returns the address of an extension map field in the struct.
|
|
||||||
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
|
|
||||||
return structPointer_ifield(p, f).(*XXX_InternalExtensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtMap returns the address of an extension map field in the struct.
|
|
||||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
|
||||||
return structPointer_ifield(p, f).(*map[int32]Extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
|
||||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
|
||||||
return structPointer_field(p, f).Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetStructPointer writes a *struct field in the struct.
|
|
||||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
|
||||||
structPointer_field(p, f).Set(q.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStructPointer reads a *struct field in the struct.
|
|
||||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
|
||||||
return structPointer{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructPointerSlice the address of a []*struct field in the struct.
|
|
||||||
func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice {
|
|
||||||
return structPointerSlice{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A structPointerSlice represents the address of a slice of pointers to structs
|
|
||||||
// (themselves messages or groups). That is, v.Type() is *[]*struct{...}.
|
|
||||||
type structPointerSlice struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p structPointerSlice) Len() int { return p.v.Len() }
|
|
||||||
func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
|
|
||||||
func (p structPointerSlice) Append(q structPointer) {
|
|
||||||
p.v.Set(reflect.Append(p.v, q.v))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
int32Type = reflect.TypeOf(int32(0))
|
|
||||||
uint32Type = reflect.TypeOf(uint32(0))
|
|
||||||
float32Type = reflect.TypeOf(float32(0))
|
|
||||||
int64Type = reflect.TypeOf(int64(0))
|
|
||||||
uint64Type = reflect.TypeOf(uint64(0))
|
|
||||||
float64Type = reflect.TypeOf(float64(0))
|
|
||||||
)
|
|
||||||
|
|
||||||
// A word32 represents a field of type *int32, *uint32, *float32, or *enum.
|
|
||||||
// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable.
|
|
||||||
type word32 struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil reports whether p is nil.
|
|
||||||
func word32_IsNil(p word32) bool {
|
|
||||||
return p.v.IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets p to point at a newly allocated word with bits set to x.
|
|
||||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
|
||||||
t := p.v.Type().Elem()
|
|
||||||
switch t {
|
|
||||||
case int32Type:
|
|
||||||
if len(o.int32s) == 0 {
|
|
||||||
o.int32s = make([]int32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.int32s[0] = int32(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.int32s[0]))
|
|
||||||
o.int32s = o.int32s[1:]
|
|
||||||
return
|
|
||||||
case uint32Type:
|
|
||||||
if len(o.uint32s) == 0 {
|
|
||||||
o.uint32s = make([]uint32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.uint32s[0] = x
|
|
||||||
p.v.Set(reflect.ValueOf(&o.uint32s[0]))
|
|
||||||
o.uint32s = o.uint32s[1:]
|
|
||||||
return
|
|
||||||
case float32Type:
|
|
||||||
if len(o.float32s) == 0 {
|
|
||||||
o.float32s = make([]float32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.float32s[0] = math.Float32frombits(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.float32s[0]))
|
|
||||||
o.float32s = o.float32s[1:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be enum
|
|
||||||
p.v.Set(reflect.New(t))
|
|
||||||
p.v.Elem().SetInt(int64(int32(x)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets the bits pointed at by p, as a uint32.
|
|
||||||
func word32_Get(p word32) uint32 {
|
|
||||||
elem := p.v.Elem()
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
return uint32(elem.Int())
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(elem.Uint())
|
|
||||||
case reflect.Float32:
|
|
||||||
return math.Float32bits(float32(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct.
|
|
||||||
func structPointer_Word32(p structPointer, f field) word32 {
|
|
||||||
return word32{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A word32Val represents a field of type int32, uint32, float32, or enum.
|
|
||||||
// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
|
|
||||||
type word32Val struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets *p to x.
|
|
||||||
func word32Val_Set(p word32Val, x uint32) {
|
|
||||||
switch p.v.Type() {
|
|
||||||
case int32Type:
|
|
||||||
p.v.SetInt(int64(x))
|
|
||||||
return
|
|
||||||
case uint32Type:
|
|
||||||
p.v.SetUint(uint64(x))
|
|
||||||
return
|
|
||||||
case float32Type:
|
|
||||||
p.v.SetFloat(float64(math.Float32frombits(x)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be enum
|
|
||||||
p.v.SetInt(int64(int32(x)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets the bits pointed at by p, as a uint32.
|
|
||||||
func word32Val_Get(p word32Val) uint32 {
|
|
||||||
elem := p.v
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
return uint32(elem.Int())
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(elem.Uint())
|
|
||||||
case reflect.Float32:
|
|
||||||
return math.Float32bits(float32(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
|
|
||||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
|
||||||
return word32Val{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A word32Slice is a slice of 32-bit values.
|
|
||||||
// That is, v.Type() is []int32, []uint32, []float32, or []enum.
|
|
||||||
type word32Slice struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word32Slice) Append(x uint32) {
|
|
||||||
n, m := p.v.Len(), p.v.Cap()
|
|
||||||
if n < m {
|
|
||||||
p.v.SetLen(n + 1)
|
|
||||||
} else {
|
|
||||||
t := p.v.Type().Elem()
|
|
||||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
|
||||||
}
|
|
||||||
elem := p.v.Index(n)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
elem.SetInt(int64(int32(x)))
|
|
||||||
case reflect.Uint32:
|
|
||||||
elem.SetUint(uint64(x))
|
|
||||||
case reflect.Float32:
|
|
||||||
elem.SetFloat(float64(math.Float32frombits(x)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word32Slice) Len() int {
|
|
||||||
return p.v.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word32Slice) Index(i int) uint32 {
|
|
||||||
elem := p.v.Index(i)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int32:
|
|
||||||
return uint32(elem.Int())
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(elem.Uint())
|
|
||||||
case reflect.Float32:
|
|
||||||
return math.Float32bits(float32(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
|
|
||||||
func structPointer_Word32Slice(p structPointer, f field) word32Slice {
|
|
||||||
return word32Slice{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64 is like word32 but for 64-bit values.
|
|
||||||
type word64 struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
|
||||||
t := p.v.Type().Elem()
|
|
||||||
switch t {
|
|
||||||
case int64Type:
|
|
||||||
if len(o.int64s) == 0 {
|
|
||||||
o.int64s = make([]int64, uint64PoolSize)
|
|
||||||
}
|
|
||||||
o.int64s[0] = int64(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.int64s[0]))
|
|
||||||
o.int64s = o.int64s[1:]
|
|
||||||
return
|
|
||||||
case uint64Type:
|
|
||||||
if len(o.uint64s) == 0 {
|
|
||||||
o.uint64s = make([]uint64, uint64PoolSize)
|
|
||||||
}
|
|
||||||
o.uint64s[0] = x
|
|
||||||
p.v.Set(reflect.ValueOf(&o.uint64s[0]))
|
|
||||||
o.uint64s = o.uint64s[1:]
|
|
||||||
return
|
|
||||||
case float64Type:
|
|
||||||
if len(o.float64s) == 0 {
|
|
||||||
o.float64s = make([]float64, uint64PoolSize)
|
|
||||||
}
|
|
||||||
o.float64s[0] = math.Float64frombits(x)
|
|
||||||
p.v.Set(reflect.ValueOf(&o.float64s[0]))
|
|
||||||
o.float64s = o.float64s[1:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64_IsNil(p word64) bool {
|
|
||||||
return p.v.IsNil()
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64_Get(p word64) uint64 {
|
|
||||||
elem := p.v.Elem()
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
return uint64(elem.Int())
|
|
||||||
case reflect.Uint64:
|
|
||||||
return elem.Uint()
|
|
||||||
case reflect.Float64:
|
|
||||||
return math.Float64bits(elem.Float())
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64(p structPointer, f field) word64 {
|
|
||||||
return word64{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64Val is like word32Val but for 64-bit values.
|
|
||||||
type word64Val struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
|
||||||
switch p.v.Type() {
|
|
||||||
case int64Type:
|
|
||||||
p.v.SetInt(int64(x))
|
|
||||||
return
|
|
||||||
case uint64Type:
|
|
||||||
p.v.SetUint(x)
|
|
||||||
return
|
|
||||||
case float64Type:
|
|
||||||
p.v.SetFloat(math.Float64frombits(x))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64Val_Get(p word64Val) uint64 {
|
|
||||||
elem := p.v
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
return uint64(elem.Int())
|
|
||||||
case reflect.Uint64:
|
|
||||||
return elem.Uint()
|
|
||||||
case reflect.Float64:
|
|
||||||
return math.Float64bits(elem.Float())
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
|
||||||
return word64Val{structPointer_field(p, f)}
|
|
||||||
}
|
|
||||||
|
|
||||||
type word64Slice struct {
|
|
||||||
v reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word64Slice) Append(x uint64) {
|
|
||||||
n, m := p.v.Len(), p.v.Cap()
|
|
||||||
if n < m {
|
|
||||||
p.v.SetLen(n + 1)
|
|
||||||
} else {
|
|
||||||
t := p.v.Type().Elem()
|
|
||||||
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
|
|
||||||
}
|
|
||||||
elem := p.v.Index(n)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
elem.SetInt(int64(int64(x)))
|
|
||||||
case reflect.Uint64:
|
|
||||||
elem.SetUint(uint64(x))
|
|
||||||
case reflect.Float64:
|
|
||||||
elem.SetFloat(float64(math.Float64frombits(x)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word64Slice) Len() int {
|
|
||||||
return p.v.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p word64Slice) Index(i int) uint64 {
|
|
||||||
elem := p.v.Index(i)
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
return uint64(elem.Int())
|
|
||||||
case reflect.Uint64:
|
|
||||||
return uint64(elem.Uint())
|
|
||||||
case reflect.Float64:
|
|
||||||
return math.Float64bits(float64(elem.Float()))
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64Slice(p structPointer, f field) word64Slice {
|
|
||||||
return word64Slice{structPointer_field(p, f)}
|
|
||||||
}
|
|
|
@ -1,270 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// +build !appengine,!js
|
|
||||||
|
|
||||||
// This file contains the implementation of the proto field accesses using package unsafe.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NOTE: These type_Foo functions would more idiomatically be methods,
|
|
||||||
// but Go does not allow methods on pointer types, and we must preserve
|
|
||||||
// some pointer type for the garbage collector. We use these
|
|
||||||
// funcs with clunky names as our poor approximation to methods.
|
|
||||||
//
|
|
||||||
// An alternative would be
|
|
||||||
// type structPointer struct { p unsafe.Pointer }
|
|
||||||
// but that does not registerize as well.
|
|
||||||
|
|
||||||
// A structPointer is a pointer to a struct.
|
|
||||||
type structPointer unsafe.Pointer
|
|
||||||
|
|
||||||
// toStructPointer returns a structPointer equivalent to the given reflect value.
|
|
||||||
func toStructPointer(v reflect.Value) structPointer {
|
|
||||||
return structPointer(unsafe.Pointer(v.Pointer()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNil reports whether p is nil.
|
|
||||||
func structPointer_IsNil(p structPointer) bool {
|
|
||||||
return p == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface returns the struct pointer, assumed to have element type t,
|
|
||||||
// as an interface value.
|
|
||||||
func structPointer_Interface(p structPointer, t reflect.Type) interface{} {
|
|
||||||
return reflect.NewAt(t, unsafe.Pointer(p)).Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A field identifies a field in a struct, accessible from a structPointer.
|
|
||||||
// In this implementation, a field is identified by its byte offset from the start of the struct.
|
|
||||||
type field uintptr
|
|
||||||
|
|
||||||
// toField returns a field equivalent to the given reflect field.
|
|
||||||
func toField(f *reflect.StructField) field {
|
|
||||||
return field(f.Offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// invalidField is an invalid field identifier.
|
|
||||||
const invalidField = ^field(0)
|
|
||||||
|
|
||||||
// IsValid reports whether the field identifier is valid.
|
|
||||||
func (f field) IsValid() bool {
|
|
||||||
return f != ^field(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the address of a []byte field in the struct.
|
|
||||||
func structPointer_Bytes(p structPointer, f field) *[]byte {
|
|
||||||
return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BytesSlice returns the address of a [][]byte field in the struct.
|
|
||||||
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
|
|
||||||
return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool returns the address of a *bool field in the struct.
|
|
||||||
func structPointer_Bool(p structPointer, f field) **bool {
|
|
||||||
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolVal returns the address of a bool field in the struct.
|
|
||||||
func structPointer_BoolVal(p structPointer, f field) *bool {
|
|
||||||
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolSlice returns the address of a []bool field in the struct.
|
|
||||||
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
|
|
||||||
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the address of a *string field in the struct.
|
|
||||||
func structPointer_String(p structPointer, f field) **string {
|
|
||||||
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringVal returns the address of a string field in the struct.
|
|
||||||
func structPointer_StringVal(p structPointer, f field) *string {
|
|
||||||
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice returns the address of a []string field in the struct.
|
|
||||||
func structPointer_StringSlice(p structPointer, f field) *[]string {
|
|
||||||
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtMap returns the address of an extension map field in the struct.
|
|
||||||
func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions {
|
|
||||||
return (*XXX_InternalExtensions)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
|
|
||||||
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAt returns the reflect.Value for a pointer to a field in the struct.
|
|
||||||
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
|
|
||||||
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetStructPointer writes a *struct field in the struct.
|
|
||||||
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
|
|
||||||
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStructPointer reads a *struct field in the struct.
|
|
||||||
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
|
|
||||||
return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructPointerSlice the address of a []*struct field in the struct.
|
|
||||||
func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
|
|
||||||
return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups).
|
|
||||||
type structPointerSlice []structPointer
|
|
||||||
|
|
||||||
func (v *structPointerSlice) Len() int { return len(*v) }
|
|
||||||
func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] }
|
|
||||||
func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) }
|
|
||||||
|
|
||||||
// A word32 is the address of a "pointer to 32-bit value" field.
|
|
||||||
type word32 **uint32
|
|
||||||
|
|
||||||
// IsNil reports whether *v is nil.
|
|
||||||
func word32_IsNil(p word32) bool {
|
|
||||||
return *p == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets *v to point at a newly allocated word set to x.
|
|
||||||
func word32_Set(p word32, o *Buffer, x uint32) {
|
|
||||||
if len(o.uint32s) == 0 {
|
|
||||||
o.uint32s = make([]uint32, uint32PoolSize)
|
|
||||||
}
|
|
||||||
o.uint32s[0] = x
|
|
||||||
*p = &o.uint32s[0]
|
|
||||||
o.uint32s = o.uint32s[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets the value pointed at by *v.
|
|
||||||
func word32_Get(p word32) uint32 {
|
|
||||||
return **p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
|
||||||
func structPointer_Word32(p structPointer, f field) word32 {
|
|
||||||
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A word32Val is the address of a 32-bit value field.
|
|
||||||
type word32Val *uint32
|
|
||||||
|
|
||||||
// Set sets *p to x.
|
|
||||||
func word32Val_Set(p word32Val, x uint32) {
|
|
||||||
*p = x
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets the value pointed at by p.
|
|
||||||
func word32Val_Get(p word32Val) uint32 {
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
|
|
||||||
func structPointer_Word32Val(p structPointer, f field) word32Val {
|
|
||||||
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// A word32Slice is a slice of 32-bit values.
|
|
||||||
type word32Slice []uint32
|
|
||||||
|
|
||||||
func (v *word32Slice) Append(x uint32) { *v = append(*v, x) }
|
|
||||||
func (v *word32Slice) Len() int { return len(*v) }
|
|
||||||
func (v *word32Slice) Index(i int) uint32 { return (*v)[i] }
|
|
||||||
|
|
||||||
// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct.
|
|
||||||
func structPointer_Word32Slice(p structPointer, f field) *word32Slice {
|
|
||||||
return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64 is like word32 but for 64-bit values.
|
|
||||||
type word64 **uint64
|
|
||||||
|
|
||||||
func word64_Set(p word64, o *Buffer, x uint64) {
|
|
||||||
if len(o.uint64s) == 0 {
|
|
||||||
o.uint64s = make([]uint64, uint64PoolSize)
|
|
||||||
}
|
|
||||||
o.uint64s[0] = x
|
|
||||||
*p = &o.uint64s[0]
|
|
||||||
o.uint64s = o.uint64s[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64_IsNil(p word64) bool {
|
|
||||||
return *p == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64_Get(p word64) uint64 {
|
|
||||||
return **p
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64(p structPointer, f field) word64 {
|
|
||||||
return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64Val is like word32Val but for 64-bit values.
|
|
||||||
type word64Val *uint64
|
|
||||||
|
|
||||||
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
|
|
||||||
*p = x
|
|
||||||
}
|
|
||||||
|
|
||||||
func word64Val_Get(p word64Val) uint64 {
|
|
||||||
return *p
|
|
||||||
}
|
|
||||||
|
|
||||||
func structPointer_Word64Val(p structPointer, f field) word64Val {
|
|
||||||
return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// word64Slice is like word32Slice but for 64-bit values.
|
|
||||||
type word64Slice []uint64
|
|
||||||
|
|
||||||
func (v *word64Slice) Append(x uint64) { *v = append(*v, x) }
|
|
||||||
func (v *word64Slice) Len() int { return len(*v) }
|
|
||||||
func (v *word64Slice) Index(i int) uint64 { return (*v)[i] }
|
|
||||||
|
|
||||||
func structPointer_Word64Slice(p structPointer, f field) *word64Slice {
|
|
||||||
return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
|
|
||||||
}
|
|
|
@ -1,872 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Routines for encoding data into the wire format for protocol buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const debug bool = false
|
|
||||||
|
|
||||||
// Constants that identify the encoding of a value on the wire.
|
|
||||||
const (
|
|
||||||
WireVarint = 0
|
|
||||||
WireFixed64 = 1
|
|
||||||
WireBytes = 2
|
|
||||||
WireStartGroup = 3
|
|
||||||
WireEndGroup = 4
|
|
||||||
WireFixed32 = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
const startSize = 10 // initial slice/string sizes
|
|
||||||
|
|
||||||
// Encoders are defined in encode.go
|
|
||||||
// An encoder outputs the full representation of a field, including its
|
|
||||||
// tag and encoder type.
|
|
||||||
type encoder func(p *Buffer, prop *Properties, base structPointer) error
|
|
||||||
|
|
||||||
// A valueEncoder encodes a single integer in a particular encoding.
|
|
||||||
type valueEncoder func(o *Buffer, x uint64) error
|
|
||||||
|
|
||||||
// Sizers are defined in encode.go
|
|
||||||
// A sizer returns the encoded size of a field, including its tag and encoder
|
|
||||||
// type.
|
|
||||||
type sizer func(prop *Properties, base structPointer) int
|
|
||||||
|
|
||||||
// A valueSizer returns the encoded size of a single integer in a particular
|
|
||||||
// encoding.
|
|
||||||
type valueSizer func(x uint64) int
|
|
||||||
|
|
||||||
// Decoders are defined in decode.go
|
|
||||||
// A decoder creates a value from its wire representation.
|
|
||||||
// Unrecognized subelements are saved in unrec.
|
|
||||||
type decoder func(p *Buffer, prop *Properties, base structPointer) error
|
|
||||||
|
|
||||||
// A valueDecoder decodes a single integer in a particular encoding.
|
|
||||||
type valueDecoder func(o *Buffer) (x uint64, err error)
|
|
||||||
|
|
||||||
// A oneofMarshaler does the marshaling for all oneof fields in a message.
|
|
||||||
type oneofMarshaler func(Message, *Buffer) error
|
|
||||||
|
|
||||||
// A oneofUnmarshaler does the unmarshaling for a oneof field in a message.
|
|
||||||
type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error)
|
|
||||||
|
|
||||||
// A oneofSizer does the sizing for all oneof fields in a message.
|
|
||||||
type oneofSizer func(Message) int
|
|
||||||
|
|
||||||
// tagMap is an optimization over map[int]int for typical protocol buffer
|
|
||||||
// use-cases. Encoded protocol buffers are often in tag order with small tag
|
|
||||||
// numbers.
|
|
||||||
type tagMap struct {
|
|
||||||
fastTags []int
|
|
||||||
slowTags map[int]int
|
|
||||||
}
|
|
||||||
|
|
||||||
// tagMapFastLimit is the upper bound on the tag number that will be stored in
|
|
||||||
// the tagMap slice rather than its map.
|
|
||||||
const tagMapFastLimit = 1024
|
|
||||||
|
|
||||||
func (p *tagMap) get(t int) (int, bool) {
|
|
||||||
if t > 0 && t < tagMapFastLimit {
|
|
||||||
if t >= len(p.fastTags) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
fi := p.fastTags[t]
|
|
||||||
return fi, fi >= 0
|
|
||||||
}
|
|
||||||
fi, ok := p.slowTags[t]
|
|
||||||
return fi, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tagMap) put(t int, fi int) {
|
|
||||||
if t > 0 && t < tagMapFastLimit {
|
|
||||||
for len(p.fastTags) < t+1 {
|
|
||||||
p.fastTags = append(p.fastTags, -1)
|
|
||||||
}
|
|
||||||
p.fastTags[t] = fi
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.slowTags == nil {
|
|
||||||
p.slowTags = make(map[int]int)
|
|
||||||
}
|
|
||||||
p.slowTags[t] = fi
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructProperties represents properties for all the fields of a struct.
|
|
||||||
// decoderTags and decoderOrigNames should only be used by the decoder.
|
|
||||||
type StructProperties struct {
|
|
||||||
Prop []*Properties // properties for each field
|
|
||||||
reqCount int // required count
|
|
||||||
decoderTags tagMap // map from proto tag to struct field number
|
|
||||||
decoderOrigNames map[string]int // map from original name to struct field number
|
|
||||||
order []int // list of struct field numbers in tag order
|
|
||||||
unrecField field // field id of the XXX_unrecognized []byte field
|
|
||||||
extendable bool // is this an extendable proto
|
|
||||||
|
|
||||||
oneofMarshaler oneofMarshaler
|
|
||||||
oneofUnmarshaler oneofUnmarshaler
|
|
||||||
oneofSizer oneofSizer
|
|
||||||
stype reflect.Type
|
|
||||||
|
|
||||||
// OneofTypes contains information about the oneof fields in this message.
|
|
||||||
// It is keyed by the original name of a field.
|
|
||||||
OneofTypes map[string]*OneofProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
// OneofProperties represents information about a specific field in a oneof.
|
|
||||||
type OneofProperties struct {
|
|
||||||
Type reflect.Type // pointer to generated struct type for this oneof field
|
|
||||||
Field int // struct field number of the containing oneof in the message
|
|
||||||
Prop *Properties
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
|
|
||||||
// See encode.go, (*Buffer).enc_struct.
|
|
||||||
|
|
||||||
func (sp *StructProperties) Len() int { return len(sp.order) }
|
|
||||||
func (sp *StructProperties) Less(i, j int) bool {
|
|
||||||
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
|
|
||||||
}
|
|
||||||
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
|
|
||||||
|
|
||||||
// Properties represents the protocol-specific behavior of a single struct field.
|
|
||||||
type Properties struct {
|
|
||||||
Name string // name of the field, for error messages
|
|
||||||
OrigName string // original name before protocol compiler (always set)
|
|
||||||
JSONName string // name to use for JSON; determined by protoc
|
|
||||||
Wire string
|
|
||||||
WireType int
|
|
||||||
Tag int
|
|
||||||
Required bool
|
|
||||||
Optional bool
|
|
||||||
Repeated bool
|
|
||||||
Packed bool // relevant for repeated primitives only
|
|
||||||
Enum string // set for enum types only
|
|
||||||
proto3 bool // whether this is known to be a proto3 field; set for []byte only
|
|
||||||
oneof bool // whether this is a oneof field
|
|
||||||
|
|
||||||
Default string // default value
|
|
||||||
HasDefault bool // whether an explicit default was provided
|
|
||||||
def_uint64 uint64
|
|
||||||
|
|
||||||
enc encoder
|
|
||||||
valEnc valueEncoder // set for bool and numeric types only
|
|
||||||
field field
|
|
||||||
tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
|
|
||||||
tagbuf [8]byte
|
|
||||||
stype reflect.Type // set for struct types only
|
|
||||||
sprop *StructProperties // set for struct types only
|
|
||||||
isMarshaler bool
|
|
||||||
isUnmarshaler bool
|
|
||||||
|
|
||||||
mtype reflect.Type // set for map types only
|
|
||||||
mkeyprop *Properties // set for map types only
|
|
||||||
mvalprop *Properties // set for map types only
|
|
||||||
|
|
||||||
size sizer
|
|
||||||
valSize valueSizer // set for bool and numeric types only
|
|
||||||
|
|
||||||
dec decoder
|
|
||||||
valDec valueDecoder // set for bool and numeric types only
|
|
||||||
|
|
||||||
// If this is a packable field, this will be the decoder for the packed version of the field.
|
|
||||||
packedDec decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// String formats the properties in the protobuf struct field tag style.
|
|
||||||
func (p *Properties) String() string {
|
|
||||||
s := p.Wire
|
|
||||||
s = ","
|
|
||||||
s += strconv.Itoa(p.Tag)
|
|
||||||
if p.Required {
|
|
||||||
s += ",req"
|
|
||||||
}
|
|
||||||
if p.Optional {
|
|
||||||
s += ",opt"
|
|
||||||
}
|
|
||||||
if p.Repeated {
|
|
||||||
s += ",rep"
|
|
||||||
}
|
|
||||||
if p.Packed {
|
|
||||||
s += ",packed"
|
|
||||||
}
|
|
||||||
s += ",name=" + p.OrigName
|
|
||||||
if p.JSONName != p.OrigName {
|
|
||||||
s += ",json=" + p.JSONName
|
|
||||||
}
|
|
||||||
if p.proto3 {
|
|
||||||
s += ",proto3"
|
|
||||||
}
|
|
||||||
if p.oneof {
|
|
||||||
s += ",oneof"
|
|
||||||
}
|
|
||||||
if len(p.Enum) > 0 {
|
|
||||||
s += ",enum=" + p.Enum
|
|
||||||
}
|
|
||||||
if p.HasDefault {
|
|
||||||
s += ",def=" + p.Default
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
|
||||||
func (p *Properties) Parse(s string) {
|
|
||||||
// "bytes,49,opt,name=foo,def=hello!"
|
|
||||||
fields := strings.Split(s, ",") // breaks def=, but handled below.
|
|
||||||
if len(fields) < 2 {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Wire = fields[0]
|
|
||||||
switch p.Wire {
|
|
||||||
case "varint":
|
|
||||||
p.WireType = WireVarint
|
|
||||||
p.valEnc = (*Buffer).EncodeVarint
|
|
||||||
p.valDec = (*Buffer).DecodeVarint
|
|
||||||
p.valSize = sizeVarint
|
|
||||||
case "fixed32":
|
|
||||||
p.WireType = WireFixed32
|
|
||||||
p.valEnc = (*Buffer).EncodeFixed32
|
|
||||||
p.valDec = (*Buffer).DecodeFixed32
|
|
||||||
p.valSize = sizeFixed32
|
|
||||||
case "fixed64":
|
|
||||||
p.WireType = WireFixed64
|
|
||||||
p.valEnc = (*Buffer).EncodeFixed64
|
|
||||||
p.valDec = (*Buffer).DecodeFixed64
|
|
||||||
p.valSize = sizeFixed64
|
|
||||||
case "zigzag32":
|
|
||||||
p.WireType = WireVarint
|
|
||||||
p.valEnc = (*Buffer).EncodeZigzag32
|
|
||||||
p.valDec = (*Buffer).DecodeZigzag32
|
|
||||||
p.valSize = sizeZigzag32
|
|
||||||
case "zigzag64":
|
|
||||||
p.WireType = WireVarint
|
|
||||||
p.valEnc = (*Buffer).EncodeZigzag64
|
|
||||||
p.valDec = (*Buffer).DecodeZigzag64
|
|
||||||
p.valSize = sizeZigzag64
|
|
||||||
case "bytes", "group":
|
|
||||||
p.WireType = WireBytes
|
|
||||||
// no numeric converter for non-numeric types
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
p.Tag, err = strconv.Atoi(fields[1])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 2; i < len(fields); i++ {
|
|
||||||
f := fields[i]
|
|
||||||
switch {
|
|
||||||
case f == "req":
|
|
||||||
p.Required = true
|
|
||||||
case f == "opt":
|
|
||||||
p.Optional = true
|
|
||||||
case f == "rep":
|
|
||||||
p.Repeated = true
|
|
||||||
case f == "packed":
|
|
||||||
p.Packed = true
|
|
||||||
case strings.HasPrefix(f, "name="):
|
|
||||||
p.OrigName = f[5:]
|
|
||||||
case strings.HasPrefix(f, "json="):
|
|
||||||
p.JSONName = f[5:]
|
|
||||||
case strings.HasPrefix(f, "enum="):
|
|
||||||
p.Enum = f[5:]
|
|
||||||
case f == "proto3":
|
|
||||||
p.proto3 = true
|
|
||||||
case f == "oneof":
|
|
||||||
p.oneof = true
|
|
||||||
case strings.HasPrefix(f, "def="):
|
|
||||||
p.HasDefault = true
|
|
||||||
p.Default = f[4:] // rest of string
|
|
||||||
if i+1 < len(fields) {
|
|
||||||
// Commas aren't escaped, and def is always last.
|
|
||||||
p.Default += "," + strings.Join(fields[i+1:], ",")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func logNoSliceEnc(t1, t2 reflect.Type) {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
|
|
||||||
}
|
|
||||||
|
|
||||||
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
|
|
||||||
|
|
||||||
// Initialize the fields for encoding and decoding.
|
|
||||||
func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
|
|
||||||
p.enc = nil
|
|
||||||
p.dec = nil
|
|
||||||
p.size = nil
|
|
||||||
|
|
||||||
switch t1 := typ; t1.Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
|
|
||||||
|
|
||||||
// proto3 scalar types
|
|
||||||
|
|
||||||
case reflect.Bool:
|
|
||||||
p.enc = (*Buffer).enc_proto3_bool
|
|
||||||
p.dec = (*Buffer).dec_proto3_bool
|
|
||||||
p.size = size_proto3_bool
|
|
||||||
case reflect.Int32:
|
|
||||||
p.enc = (*Buffer).enc_proto3_int32
|
|
||||||
p.dec = (*Buffer).dec_proto3_int32
|
|
||||||
p.size = size_proto3_int32
|
|
||||||
case reflect.Uint32:
|
|
||||||
p.enc = (*Buffer).enc_proto3_uint32
|
|
||||||
p.dec = (*Buffer).dec_proto3_int32 // can reuse
|
|
||||||
p.size = size_proto3_uint32
|
|
||||||
case reflect.Int64, reflect.Uint64:
|
|
||||||
p.enc = (*Buffer).enc_proto3_int64
|
|
||||||
p.dec = (*Buffer).dec_proto3_int64
|
|
||||||
p.size = size_proto3_int64
|
|
||||||
case reflect.Float32:
|
|
||||||
p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_proto3_int32
|
|
||||||
p.size = size_proto3_uint32
|
|
||||||
case reflect.Float64:
|
|
||||||
p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_proto3_int64
|
|
||||||
p.size = size_proto3_int64
|
|
||||||
case reflect.String:
|
|
||||||
p.enc = (*Buffer).enc_proto3_string
|
|
||||||
p.dec = (*Buffer).dec_proto3_string
|
|
||||||
p.size = size_proto3_string
|
|
||||||
|
|
||||||
case reflect.Ptr:
|
|
||||||
switch t2 := t1.Elem(); t2.Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
|
|
||||||
break
|
|
||||||
case reflect.Bool:
|
|
||||||
p.enc = (*Buffer).enc_bool
|
|
||||||
p.dec = (*Buffer).dec_bool
|
|
||||||
p.size = size_bool
|
|
||||||
case reflect.Int32:
|
|
||||||
p.enc = (*Buffer).enc_int32
|
|
||||||
p.dec = (*Buffer).dec_int32
|
|
||||||
p.size = size_int32
|
|
||||||
case reflect.Uint32:
|
|
||||||
p.enc = (*Buffer).enc_uint32
|
|
||||||
p.dec = (*Buffer).dec_int32 // can reuse
|
|
||||||
p.size = size_uint32
|
|
||||||
case reflect.Int64, reflect.Uint64:
|
|
||||||
p.enc = (*Buffer).enc_int64
|
|
||||||
p.dec = (*Buffer).dec_int64
|
|
||||||
p.size = size_int64
|
|
||||||
case reflect.Float32:
|
|
||||||
p.enc = (*Buffer).enc_uint32 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_int32
|
|
||||||
p.size = size_uint32
|
|
||||||
case reflect.Float64:
|
|
||||||
p.enc = (*Buffer).enc_int64 // can just treat them as bits
|
|
||||||
p.dec = (*Buffer).dec_int64
|
|
||||||
p.size = size_int64
|
|
||||||
case reflect.String:
|
|
||||||
p.enc = (*Buffer).enc_string
|
|
||||||
p.dec = (*Buffer).dec_string
|
|
||||||
p.size = size_string
|
|
||||||
case reflect.Struct:
|
|
||||||
p.stype = t1.Elem()
|
|
||||||
p.isMarshaler = isMarshaler(t1)
|
|
||||||
p.isUnmarshaler = isUnmarshaler(t1)
|
|
||||||
if p.Wire == "bytes" {
|
|
||||||
p.enc = (*Buffer).enc_struct_message
|
|
||||||
p.dec = (*Buffer).dec_struct_message
|
|
||||||
p.size = size_struct_message
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_struct_group
|
|
||||||
p.dec = (*Buffer).dec_struct_group
|
|
||||||
p.size = size_struct_group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
switch t2 := t1.Elem(); t2.Kind() {
|
|
||||||
default:
|
|
||||||
logNoSliceEnc(t1, t2)
|
|
||||||
break
|
|
||||||
case reflect.Bool:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_bool
|
|
||||||
p.size = size_slice_packed_bool
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_bool
|
|
||||||
p.size = size_slice_bool
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_bool
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_bool
|
|
||||||
case reflect.Int32:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_int32
|
|
||||||
p.size = size_slice_packed_int32
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_int32
|
|
||||||
p.size = size_slice_int32
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int32
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
|
||||||
case reflect.Uint32:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
|
||||||
p.size = size_slice_packed_uint32
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_uint32
|
|
||||||
p.size = size_slice_uint32
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int32
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
|
||||||
case reflect.Int64, reflect.Uint64:
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_int64
|
|
||||||
p.size = size_slice_packed_int64
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_int64
|
|
||||||
p.size = size_slice_int64
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int64
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
|
||||||
case reflect.Uint8:
|
|
||||||
p.dec = (*Buffer).dec_slice_byte
|
|
||||||
if p.proto3 {
|
|
||||||
p.enc = (*Buffer).enc_proto3_slice_byte
|
|
||||||
p.size = size_proto3_slice_byte
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_byte
|
|
||||||
p.size = size_slice_byte
|
|
||||||
}
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
switch t2.Bits() {
|
|
||||||
case 32:
|
|
||||||
// can just treat them as bits
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_uint32
|
|
||||||
p.size = size_slice_packed_uint32
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_uint32
|
|
||||||
p.size = size_slice_uint32
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int32
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int32
|
|
||||||
case 64:
|
|
||||||
// can just treat them as bits
|
|
||||||
if p.Packed {
|
|
||||||
p.enc = (*Buffer).enc_slice_packed_int64
|
|
||||||
p.size = size_slice_packed_int64
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_int64
|
|
||||||
p.size = size_slice_int64
|
|
||||||
}
|
|
||||||
p.dec = (*Buffer).dec_slice_int64
|
|
||||||
p.packedDec = (*Buffer).dec_slice_packed_int64
|
|
||||||
default:
|
|
||||||
logNoSliceEnc(t1, t2)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
p.enc = (*Buffer).enc_slice_string
|
|
||||||
p.dec = (*Buffer).dec_slice_string
|
|
||||||
p.size = size_slice_string
|
|
||||||
case reflect.Ptr:
|
|
||||||
switch t3 := t2.Elem(); t3.Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
|
|
||||||
break
|
|
||||||
case reflect.Struct:
|
|
||||||
p.stype = t2.Elem()
|
|
||||||
p.isMarshaler = isMarshaler(t2)
|
|
||||||
p.isUnmarshaler = isUnmarshaler(t2)
|
|
||||||
if p.Wire == "bytes" {
|
|
||||||
p.enc = (*Buffer).enc_slice_struct_message
|
|
||||||
p.dec = (*Buffer).dec_slice_struct_message
|
|
||||||
p.size = size_slice_struct_message
|
|
||||||
} else {
|
|
||||||
p.enc = (*Buffer).enc_slice_struct_group
|
|
||||||
p.dec = (*Buffer).dec_slice_struct_group
|
|
||||||
p.size = size_slice_struct_group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
switch t2.Elem().Kind() {
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
|
|
||||||
break
|
|
||||||
case reflect.Uint8:
|
|
||||||
p.enc = (*Buffer).enc_slice_slice_byte
|
|
||||||
p.dec = (*Buffer).dec_slice_slice_byte
|
|
||||||
p.size = size_slice_slice_byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
p.enc = (*Buffer).enc_new_map
|
|
||||||
p.dec = (*Buffer).dec_new_map
|
|
||||||
p.size = size_new_map
|
|
||||||
|
|
||||||
p.mtype = t1
|
|
||||||
p.mkeyprop = &Properties{}
|
|
||||||
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
|
|
||||||
p.mvalprop = &Properties{}
|
|
||||||
vtype := p.mtype.Elem()
|
|
||||||
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
|
|
||||||
// The value type is not a message (*T) or bytes ([]byte),
|
|
||||||
// so we need encoders for the pointer to this type.
|
|
||||||
vtype = reflect.PtrTo(vtype)
|
|
||||||
}
|
|
||||||
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// precalculate tag code
|
|
||||||
wire := p.WireType
|
|
||||||
if p.Packed {
|
|
||||||
wire = WireBytes
|
|
||||||
}
|
|
||||||
x := uint32(p.Tag)<<3 | uint32(wire)
|
|
||||||
i := 0
|
|
||||||
for i = 0; x > 127; i++ {
|
|
||||||
p.tagbuf[i] = 0x80 | uint8(x&0x7F)
|
|
||||||
x >>= 7
|
|
||||||
}
|
|
||||||
p.tagbuf[i] = uint8(x)
|
|
||||||
p.tagcode = p.tagbuf[0 : i+1]
|
|
||||||
|
|
||||||
if p.stype != nil {
|
|
||||||
if lockGetProp {
|
|
||||||
p.sprop = GetProperties(p.stype)
|
|
||||||
} else {
|
|
||||||
p.sprop = getPropertiesLocked(p.stype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
|
||||||
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
|
||||||
)
|
|
||||||
|
|
||||||
// isMarshaler reports whether type t implements Marshaler.
|
|
||||||
func isMarshaler(t reflect.Type) bool {
|
|
||||||
// We're checking for (likely) pointer-receiver methods
|
|
||||||
// so if t is not a pointer, something is very wrong.
|
|
||||||
// The calls above only invoke isMarshaler on pointer types.
|
|
||||||
if t.Kind() != reflect.Ptr {
|
|
||||||
panic("proto: misuse of isMarshaler")
|
|
||||||
}
|
|
||||||
return t.Implements(marshalerType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// isUnmarshaler reports whether type t implements Unmarshaler.
|
|
||||||
func isUnmarshaler(t reflect.Type) bool {
|
|
||||||
// We're checking for (likely) pointer-receiver methods
|
|
||||||
// so if t is not a pointer, something is very wrong.
|
|
||||||
// The calls above only invoke isUnmarshaler on pointer types.
|
|
||||||
if t.Kind() != reflect.Ptr {
|
|
||||||
panic("proto: misuse of isUnmarshaler")
|
|
||||||
}
|
|
||||||
return t.Implements(unmarshalerType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init populates the properties from a protocol buffer struct tag.
|
|
||||||
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
|
||||||
p.init(typ, name, tag, f, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
|
|
||||||
// "bytes,49,opt,def=hello!"
|
|
||||||
p.Name = name
|
|
||||||
p.OrigName = name
|
|
||||||
if f != nil {
|
|
||||||
p.field = toField(f)
|
|
||||||
}
|
|
||||||
if tag == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.Parse(tag)
|
|
||||||
p.setEncAndDec(typ, f, lockGetProp)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
propertiesMu sync.RWMutex
|
|
||||||
propertiesMap = make(map[reflect.Type]*StructProperties)
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetProperties returns the list of properties for the type represented by t.
|
|
||||||
// t must represent a generated struct type of a protocol message.
|
|
||||||
func GetProperties(t reflect.Type) *StructProperties {
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
panic("proto: type must have kind struct")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most calls to GetProperties in a long-running program will be
|
|
||||||
// retrieving details for types we have seen before.
|
|
||||||
propertiesMu.RLock()
|
|
||||||
sprop, ok := propertiesMap[t]
|
|
||||||
propertiesMu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
if collectStats {
|
|
||||||
stats.Chit++
|
|
||||||
}
|
|
||||||
return sprop
|
|
||||||
}
|
|
||||||
|
|
||||||
propertiesMu.Lock()
|
|
||||||
sprop = getPropertiesLocked(t)
|
|
||||||
propertiesMu.Unlock()
|
|
||||||
return sprop
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPropertiesLocked requires that propertiesMu is held.
|
|
||||||
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
|
||||||
if prop, ok := propertiesMap[t]; ok {
|
|
||||||
if collectStats {
|
|
||||||
stats.Chit++
|
|
||||||
}
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
if collectStats {
|
|
||||||
stats.Cmiss++
|
|
||||||
}
|
|
||||||
|
|
||||||
prop := new(StructProperties)
|
|
||||||
// in case of recursive protos, fill this in now.
|
|
||||||
propertiesMap[t] = prop
|
|
||||||
|
|
||||||
// build properties
|
|
||||||
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) ||
|
|
||||||
reflect.PtrTo(t).Implements(extendableProtoV1Type)
|
|
||||||
prop.unrecField = invalidField
|
|
||||||
prop.Prop = make([]*Properties, t.NumField())
|
|
||||||
prop.order = make([]int, t.NumField())
|
|
||||||
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
p := new(Properties)
|
|
||||||
name := f.Name
|
|
||||||
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
|
|
||||||
|
|
||||||
if f.Name == "XXX_InternalExtensions" { // special case
|
|
||||||
p.enc = (*Buffer).enc_exts
|
|
||||||
p.dec = nil // not needed
|
|
||||||
p.size = size_exts
|
|
||||||
} else if f.Name == "XXX_extensions" { // special case
|
|
||||||
p.enc = (*Buffer).enc_map
|
|
||||||
p.dec = nil // not needed
|
|
||||||
p.size = size_map
|
|
||||||
} else if f.Name == "XXX_unrecognized" { // special case
|
|
||||||
prop.unrecField = toField(&f)
|
|
||||||
}
|
|
||||||
oneof := f.Tag.Get("protobuf_oneof") // special case
|
|
||||||
if oneof != "" {
|
|
||||||
// Oneof fields don't use the traditional protobuf tag.
|
|
||||||
p.OrigName = oneof
|
|
||||||
}
|
|
||||||
prop.Prop[i] = p
|
|
||||||
prop.order[i] = i
|
|
||||||
if debug {
|
|
||||||
print(i, " ", f.Name, " ", t.String(), " ")
|
|
||||||
if p.Tag > 0 {
|
|
||||||
print(p.String())
|
|
||||||
}
|
|
||||||
print("\n")
|
|
||||||
}
|
|
||||||
if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && oneof == "" {
|
|
||||||
fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-order prop.order.
|
|
||||||
sort.Sort(prop)
|
|
||||||
|
|
||||||
type oneofMessage interface {
|
|
||||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
|
||||||
}
|
|
||||||
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
|
||||||
var oots []interface{}
|
|
||||||
prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
|
|
||||||
prop.stype = t
|
|
||||||
|
|
||||||
// Interpret oneof metadata.
|
|
||||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
|
||||||
for _, oot := range oots {
|
|
||||||
oop := &OneofProperties{
|
|
||||||
Type: reflect.ValueOf(oot).Type(), // *T
|
|
||||||
Prop: new(Properties),
|
|
||||||
}
|
|
||||||
sft := oop.Type.Elem().Field(0)
|
|
||||||
oop.Prop.Name = sft.Name
|
|
||||||
oop.Prop.Parse(sft.Tag.Get("protobuf"))
|
|
||||||
// There will be exactly one interface field that
|
|
||||||
// this new value is assignable to.
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
if f.Type.Kind() != reflect.Interface {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !oop.Type.AssignableTo(f.Type) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
oop.Field = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
prop.OneofTypes[oop.Prop.OrigName] = oop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// build required counts
|
|
||||||
// build tags
|
|
||||||
reqCount := 0
|
|
||||||
prop.decoderOrigNames = make(map[string]int)
|
|
||||||
for i, p := range prop.Prop {
|
|
||||||
if strings.HasPrefix(p.Name, "XXX_") {
|
|
||||||
// Internal fields should not appear in tags/origNames maps.
|
|
||||||
// They are handled specially when encoding and decoding.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.Required {
|
|
||||||
reqCount++
|
|
||||||
}
|
|
||||||
prop.decoderTags.put(p.Tag, i)
|
|
||||||
prop.decoderOrigNames[p.OrigName] = i
|
|
||||||
}
|
|
||||||
prop.reqCount = reqCount
|
|
||||||
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the Properties object for the x[0]'th field of the structure.
|
|
||||||
func propByIndex(t reflect.Type, x []int) *Properties {
|
|
||||||
if len(x) != 1 {
|
|
||||||
fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
prop := GetProperties(t)
|
|
||||||
return prop.Prop[x[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the address and type of a pointer to a struct from an interface.
|
|
||||||
func getbase(pb Message) (t reflect.Type, b structPointer, err error) {
|
|
||||||
if pb == nil {
|
|
||||||
err = ErrNil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// get the reflect type of the pointer to the struct.
|
|
||||||
t = reflect.TypeOf(pb)
|
|
||||||
// get the address of the struct.
|
|
||||||
value := reflect.ValueOf(pb)
|
|
||||||
b = toStructPointer(value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// A global registry of enum types.
|
|
||||||
// The generated code will register the generated maps by calling RegisterEnum.
|
|
||||||
|
|
||||||
var enumValueMaps = make(map[string]map[string]int32)
|
|
||||||
|
|
||||||
// RegisterEnum is called from the generated code to install the enum descriptor
|
|
||||||
// maps into the global table to aid parsing text format protocol buffers.
|
|
||||||
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
|
|
||||||
if _, ok := enumValueMaps[typeName]; ok {
|
|
||||||
panic("proto: duplicate enum registered: " + typeName)
|
|
||||||
}
|
|
||||||
enumValueMaps[typeName] = valueMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumValueMap returns the mapping from names to integers of the
|
|
||||||
// enum type enumType, or a nil if not found.
|
|
||||||
func EnumValueMap(enumType string) map[string]int32 {
|
|
||||||
return enumValueMaps[enumType]
|
|
||||||
}
|
|
||||||
|
|
||||||
// A registry of all linked message types.
|
|
||||||
// The string is a fully-qualified proto name ("pkg.Message").
|
|
||||||
var (
|
|
||||||
protoTypes = make(map[string]reflect.Type)
|
|
||||||
revProtoTypes = make(map[reflect.Type]string)
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterType is called from generated code and maps from the fully qualified
|
|
||||||
// proto name to the type (pointer to struct) of the protocol buffer.
|
|
||||||
func RegisterType(x Message, name string) {
|
|
||||||
if _, ok := protoTypes[name]; ok {
|
|
||||||
// TODO: Some day, make this a panic.
|
|
||||||
log.Printf("proto: duplicate proto type registered: %s", name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := reflect.TypeOf(x)
|
|
||||||
protoTypes[name] = t
|
|
||||||
revProtoTypes[t] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageName returns the fully-qualified proto name for the given message type.
|
|
||||||
func MessageName(x Message) string {
|
|
||||||
type xname interface {
|
|
||||||
XXX_MessageName() string
|
|
||||||
}
|
|
||||||
if m, ok := x.(xname); ok {
|
|
||||||
return m.XXX_MessageName()
|
|
||||||
}
|
|
||||||
return revProtoTypes[reflect.TypeOf(x)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageType returns the message type (pointer to struct) for a named message.
|
|
||||||
func MessageType(name string) reflect.Type { return protoTypes[name] }
|
|
||||||
|
|
||||||
// A registry of all linked proto files.
|
|
||||||
var (
|
|
||||||
protoFiles = make(map[string][]byte) // file name => fileDescriptor
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterFile is called from generated code and maps from the
|
|
||||||
// full file name of a .proto file to its compressed FileDescriptorProto.
|
|
||||||
func RegisterFile(filename string, fileDescriptor []byte) {
|
|
||||||
protoFiles[filename] = fileDescriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
|
|
||||||
func FileDescriptor(filename string) []byte { return protoFiles[filename] }
|
|
|
@ -1,347 +0,0 @@
|
||||||
// Code generated by protoc-gen-go.
|
|
||||||
// source: proto3_proto/proto3.proto
|
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package proto3_proto is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
proto3_proto/proto3.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
Message
|
|
||||||
Nested
|
|
||||||
MessageWithMap
|
|
||||||
IntMap
|
|
||||||
IntMaps
|
|
||||||
*/
|
|
||||||
package proto3_proto
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
import google_protobuf "github.com/golang/protobuf/ptypes/any"
|
|
||||||
import testdata "github.com/golang/protobuf/proto/testdata"
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
|
||||||
|
|
||||||
type Message_Humour int32
|
|
||||||
|
|
||||||
const (
|
|
||||||
Message_UNKNOWN Message_Humour = 0
|
|
||||||
Message_PUNS Message_Humour = 1
|
|
||||||
Message_SLAPSTICK Message_Humour = 2
|
|
||||||
Message_BILL_BAILEY Message_Humour = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
var Message_Humour_name = map[int32]string{
|
|
||||||
0: "UNKNOWN",
|
|
||||||
1: "PUNS",
|
|
||||||
2: "SLAPSTICK",
|
|
||||||
3: "BILL_BAILEY",
|
|
||||||
}
|
|
||||||
var Message_Humour_value = map[string]int32{
|
|
||||||
"UNKNOWN": 0,
|
|
||||||
"PUNS": 1,
|
|
||||||
"SLAPSTICK": 2,
|
|
||||||
"BILL_BAILEY": 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x Message_Humour) String() string {
|
|
||||||
return proto.EnumName(Message_Humour_name, int32(x))
|
|
||||||
}
|
|
||||||
func (Message_Humour) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
|
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
|
||||||
Hilarity Message_Humour `protobuf:"varint,2,opt,name=hilarity,enum=proto3_proto.Message_Humour" json:"hilarity,omitempty"`
|
|
||||||
HeightInCm uint32 `protobuf:"varint,3,opt,name=height_in_cm,json=heightInCm" json:"height_in_cm,omitempty"`
|
|
||||||
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
|
|
||||||
ResultCount int64 `protobuf:"varint,7,opt,name=result_count,json=resultCount" json:"result_count,omitempty"`
|
|
||||||
TrueScotsman bool `protobuf:"varint,8,opt,name=true_scotsman,json=trueScotsman" json:"true_scotsman,omitempty"`
|
|
||||||
Score float32 `protobuf:"fixed32,9,opt,name=score" json:"score,omitempty"`
|
|
||||||
Key []uint64 `protobuf:"varint,5,rep,packed,name=key" json:"key,omitempty"`
|
|
||||||
ShortKey []int32 `protobuf:"varint,19,rep,packed,name=short_key,json=shortKey" json:"short_key,omitempty"`
|
|
||||||
Nested *Nested `protobuf:"bytes,6,opt,name=nested" json:"nested,omitempty"`
|
|
||||||
RFunny []Message_Humour `protobuf:"varint,16,rep,packed,name=r_funny,json=rFunny,enum=proto3_proto.Message_Humour" json:"r_funny,omitempty"`
|
|
||||||
Terrain map[string]*Nested `protobuf:"bytes,10,rep,name=terrain" json:"terrain,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
|
||||||
Proto2Field *testdata.SubDefaults `protobuf:"bytes,11,opt,name=proto2_field,json=proto2Field" json:"proto2_field,omitempty"`
|
|
||||||
Proto2Value map[string]*testdata.SubDefaults `protobuf:"bytes,13,rep,name=proto2_value,json=proto2Value" json:"proto2_value,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
|
||||||
Anything *google_protobuf.Any `protobuf:"bytes,14,opt,name=anything" json:"anything,omitempty"`
|
|
||||||
ManyThings []*google_protobuf.Any `protobuf:"bytes,15,rep,name=many_things,json=manyThings" json:"many_things,omitempty"`
|
|
||||||
Submessage *Message `protobuf:"bytes,17,opt,name=submessage" json:"submessage,omitempty"`
|
|
||||||
Children []*Message `protobuf:"bytes,18,rep,name=children" json:"children,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) Reset() { *m = Message{} }
|
|
||||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Message) ProtoMessage() {}
|
|
||||||
func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
|
||||||
|
|
||||||
func (m *Message) GetName() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetHilarity() Message_Humour {
|
|
||||||
if m != nil {
|
|
||||||
return m.Hilarity
|
|
||||||
}
|
|
||||||
return Message_UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetHeightInCm() uint32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.HeightInCm
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetData() []byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.Data
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetResultCount() int64 {
|
|
||||||
if m != nil {
|
|
||||||
return m.ResultCount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetTrueScotsman() bool {
|
|
||||||
if m != nil {
|
|
||||||
return m.TrueScotsman
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetScore() float32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.Score
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetKey() []uint64 {
|
|
||||||
if m != nil {
|
|
||||||
return m.Key
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetShortKey() []int32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.ShortKey
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetNested() *Nested {
|
|
||||||
if m != nil {
|
|
||||||
return m.Nested
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetRFunny() []Message_Humour {
|
|
||||||
if m != nil {
|
|
||||||
return m.RFunny
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetTerrain() map[string]*Nested {
|
|
||||||
if m != nil {
|
|
||||||
return m.Terrain
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetProto2Field() *testdata.SubDefaults {
|
|
||||||
if m != nil {
|
|
||||||
return m.Proto2Field
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetProto2Value() map[string]*testdata.SubDefaults {
|
|
||||||
if m != nil {
|
|
||||||
return m.Proto2Value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetAnything() *google_protobuf.Any {
|
|
||||||
if m != nil {
|
|
||||||
return m.Anything
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetManyThings() []*google_protobuf.Any {
|
|
||||||
if m != nil {
|
|
||||||
return m.ManyThings
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetSubmessage() *Message {
|
|
||||||
if m != nil {
|
|
||||||
return m.Submessage
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Message) GetChildren() []*Message {
|
|
||||||
if m != nil {
|
|
||||||
return m.Children
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Nested struct {
|
|
||||||
Bunny string `protobuf:"bytes,1,opt,name=bunny" json:"bunny,omitempty"`
|
|
||||||
Cute bool `protobuf:"varint,2,opt,name=cute" json:"cute,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Nested) Reset() { *m = Nested{} }
|
|
||||||
func (m *Nested) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Nested) ProtoMessage() {}
|
|
||||||
func (*Nested) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
|
||||||
|
|
||||||
func (m *Nested) GetBunny() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Bunny
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Nested) GetCute() bool {
|
|
||||||
if m != nil {
|
|
||||||
return m.Cute
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type MessageWithMap struct {
|
|
||||||
ByteMapping map[bool][]byte `protobuf:"bytes,1,rep,name=byte_mapping,json=byteMapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
|
|
||||||
func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*MessageWithMap) ProtoMessage() {}
|
|
||||||
func (*MessageWithMap) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
|
||||||
|
|
||||||
func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
|
|
||||||
if m != nil {
|
|
||||||
return m.ByteMapping
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type IntMap struct {
|
|
||||||
Rtt map[int32]int32 `protobuf:"bytes,1,rep,name=rtt" json:"rtt,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *IntMap) Reset() { *m = IntMap{} }
|
|
||||||
func (m *IntMap) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*IntMap) ProtoMessage() {}
|
|
||||||
func (*IntMap) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
|
||||||
|
|
||||||
func (m *IntMap) GetRtt() map[int32]int32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.Rtt
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type IntMaps struct {
|
|
||||||
Maps []*IntMap `protobuf:"bytes,1,rep,name=maps" json:"maps,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *IntMaps) Reset() { *m = IntMaps{} }
|
|
||||||
func (m *IntMaps) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*IntMaps) ProtoMessage() {}
|
|
||||||
func (*IntMaps) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
|
||||||
|
|
||||||
func (m *IntMaps) GetMaps() []*IntMap {
|
|
||||||
if m != nil {
|
|
||||||
return m.Maps
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*Message)(nil), "proto3_proto.Message")
|
|
||||||
proto.RegisterType((*Nested)(nil), "proto3_proto.Nested")
|
|
||||||
proto.RegisterType((*MessageWithMap)(nil), "proto3_proto.MessageWithMap")
|
|
||||||
proto.RegisterType((*IntMap)(nil), "proto3_proto.IntMap")
|
|
||||||
proto.RegisterType((*IntMaps)(nil), "proto3_proto.IntMaps")
|
|
||||||
proto.RegisterEnum("proto3_proto.Message_Humour", Message_Humour_name, Message_Humour_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("proto3_proto/proto3.proto", fileDescriptor0) }
|
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
|
||||||
// 733 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x53, 0x6d, 0x6f, 0xf3, 0x34,
|
|
||||||
0x14, 0x25, 0x4d, 0x5f, 0xd2, 0x9b, 0x74, 0x0b, 0x5e, 0x91, 0xbc, 0x02, 0x52, 0x28, 0x12, 0x8a,
|
|
||||||
0x78, 0x49, 0xa1, 0xd3, 0xd0, 0x84, 0x10, 0x68, 0x1b, 0x9b, 0xa8, 0xd6, 0x95, 0xca, 0xdd, 0x98,
|
|
||||||
0xf8, 0x14, 0xa5, 0xad, 0xdb, 0x46, 0x34, 0x4e, 0x49, 0x1c, 0xa4, 0xfc, 0x1d, 0xfe, 0x28, 0x8f,
|
|
||||||
0x6c, 0xa7, 0x5d, 0x36, 0x65, 0xcf, 0xf3, 0x29, 0xf6, 0xf1, 0xb9, 0xf7, 0x9c, 0x1c, 0x5f, 0xc3,
|
|
||||||
0xe9, 0x2e, 0x89, 0x79, 0x7c, 0xe6, 0xcb, 0xcf, 0x40, 0x6d, 0x3c, 0xf9, 0x41, 0x56, 0xf9, 0xa8,
|
|
||||||
0x77, 0xba, 0x8e, 0xe3, 0xf5, 0x96, 0x2a, 0xca, 0x3c, 0x5b, 0x0d, 0x02, 0x96, 0x2b, 0x62, 0xef,
|
|
||||||
0x84, 0xd3, 0x94, 0x2f, 0x03, 0x1e, 0x0c, 0xc4, 0x42, 0x81, 0xfd, 0xff, 0x5b, 0xd0, 0xba, 0xa7,
|
|
||||||
0x69, 0x1a, 0xac, 0x29, 0x42, 0x50, 0x67, 0x41, 0x44, 0xb1, 0xe6, 0x68, 0x6e, 0x9b, 0xc8, 0x35,
|
|
||||||
0xba, 0x00, 0x63, 0x13, 0x6e, 0x83, 0x24, 0xe4, 0x39, 0xae, 0x39, 0x9a, 0x7b, 0x34, 0xfc, 0xcc,
|
|
||||||
0x2b, 0x0b, 0x7a, 0x45, 0xb1, 0xf7, 0x7b, 0x16, 0xc5, 0x59, 0x42, 0x0e, 0x6c, 0xe4, 0x80, 0xb5,
|
|
||||||
0xa1, 0xe1, 0x7a, 0xc3, 0xfd, 0x90, 0xf9, 0x8b, 0x08, 0xeb, 0x8e, 0xe6, 0x76, 0x08, 0x28, 0x6c,
|
|
||||||
0xc4, 0xae, 0x23, 0xa1, 0x27, 0xec, 0xe0, 0xba, 0xa3, 0xb9, 0x16, 0x91, 0x6b, 0xf4, 0x05, 0x58,
|
|
||||||
0x09, 0x4d, 0xb3, 0x2d, 0xf7, 0x17, 0x71, 0xc6, 0x38, 0x6e, 0x39, 0x9a, 0xab, 0x13, 0x53, 0x61,
|
|
||||||
0xd7, 0x02, 0x42, 0x5f, 0x42, 0x87, 0x27, 0x19, 0xf5, 0xd3, 0x45, 0xcc, 0xd3, 0x28, 0x60, 0xd8,
|
|
||||||
0x70, 0x34, 0xd7, 0x20, 0x96, 0x00, 0x67, 0x05, 0x86, 0xba, 0xd0, 0x48, 0x17, 0x71, 0x42, 0x71,
|
|
||||||
0xdb, 0xd1, 0xdc, 0x1a, 0x51, 0x1b, 0x64, 0x83, 0xfe, 0x37, 0xcd, 0x71, 0xc3, 0xd1, 0xdd, 0x3a,
|
|
||||||
0x11, 0x4b, 0xf4, 0x29, 0xb4, 0xd3, 0x4d, 0x9c, 0x70, 0x5f, 0xe0, 0x27, 0x8e, 0xee, 0x36, 0x88,
|
|
||||||
0x21, 0x81, 0x3b, 0x9a, 0xa3, 0x6f, 0xa1, 0xc9, 0x68, 0xca, 0xe9, 0x12, 0x37, 0x1d, 0xcd, 0x35,
|
|
||||||
0x87, 0xdd, 0x97, 0xbf, 0x3e, 0x91, 0x67, 0xa4, 0xe0, 0xa0, 0x73, 0x68, 0x25, 0xfe, 0x2a, 0x63,
|
|
||||||
0x2c, 0xc7, 0xb6, 0xa3, 0x7f, 0x30, 0xa9, 0x66, 0x72, 0x2b, 0xb8, 0xe8, 0x67, 0x68, 0x71, 0x9a,
|
|
||||||
0x24, 0x41, 0xc8, 0x30, 0x38, 0xba, 0x6b, 0x0e, 0xfb, 0xd5, 0x65, 0x0f, 0x8a, 0x74, 0xc3, 0x78,
|
|
||||||
0x92, 0x93, 0x7d, 0x09, 0xba, 0x00, 0x75, 0xff, 0x43, 0x7f, 0x15, 0xd2, 0xed, 0x12, 0x9b, 0xd2,
|
|
||||||
0xe8, 0x27, 0xde, 0xfe, 0xae, 0xbd, 0x59, 0x36, 0xff, 0x8d, 0xae, 0x82, 0x6c, 0xcb, 0x53, 0x62,
|
|
||||||
0x2a, 0xea, 0xad, 0x60, 0xa2, 0xd1, 0xa1, 0xf2, 0xdf, 0x60, 0x9b, 0x51, 0xdc, 0x91, 0xe2, 0x5f,
|
|
||||||
0x55, 0x8b, 0x4f, 0x25, 0xf3, 0x4f, 0x41, 0x54, 0x06, 0x8a, 0x56, 0x12, 0x41, 0xdf, 0x83, 0x11,
|
|
||||||
0xb0, 0x9c, 0x6f, 0x42, 0xb6, 0xc6, 0x47, 0x45, 0x52, 0x6a, 0x0e, 0xbd, 0xfd, 0x1c, 0x7a, 0x97,
|
|
||||||
0x2c, 0x27, 0x07, 0x16, 0x3a, 0x07, 0x33, 0x0a, 0x58, 0xee, 0xcb, 0x5d, 0x8a, 0x8f, 0xa5, 0x76,
|
|
||||||
0x75, 0x11, 0x08, 0xe2, 0x83, 0xe4, 0xa1, 0x73, 0x80, 0x34, 0x9b, 0x47, 0xca, 0x14, 0xfe, 0xb8,
|
|
||||||
0xf8, 0xd7, 0x2a, 0xc7, 0xa4, 0x44, 0x44, 0x3f, 0x80, 0xb1, 0xd8, 0x84, 0xdb, 0x65, 0x42, 0x19,
|
|
||||||
0x46, 0x52, 0xea, 0x8d, 0xa2, 0x03, 0xad, 0x37, 0x05, 0xab, 0x1c, 0xf8, 0x7e, 0x72, 0xd4, 0xd3,
|
|
||||||
0x90, 0x93, 0xf3, 0x35, 0x34, 0x54, 0x70, 0xb5, 0xf7, 0xcc, 0x86, 0xa2, 0xfc, 0x54, 0xbb, 0xd0,
|
|
||||||
0x7a, 0x8f, 0x60, 0xbf, 0x4e, 0xb1, 0xa2, 0xeb, 0x37, 0x2f, 0xbb, 0xbe, 0x71, 0x91, 0xcf, 0x6d,
|
|
||||||
0xfb, 0xbf, 0x42, 0x53, 0x0d, 0x14, 0x32, 0xa1, 0xf5, 0x38, 0xb9, 0x9b, 0xfc, 0xf1, 0x34, 0xb1,
|
|
||||||
0x3f, 0x42, 0x06, 0xd4, 0xa7, 0x8f, 0x93, 0x99, 0xad, 0xa1, 0x0e, 0xb4, 0x67, 0xe3, 0xcb, 0xe9,
|
|
||||||
0xec, 0x61, 0x74, 0x7d, 0x67, 0xd7, 0xd0, 0x31, 0x98, 0x57, 0xa3, 0xf1, 0xd8, 0xbf, 0xba, 0x1c,
|
|
||||||
0x8d, 0x6f, 0xfe, 0xb2, 0xf5, 0xfe, 0x10, 0x9a, 0xca, 0xac, 0x78, 0x33, 0x73, 0x39, 0xbe, 0xca,
|
|
||||||
0x8f, 0xda, 0x88, 0x57, 0xba, 0xc8, 0xb8, 0x32, 0x64, 0x10, 0xb9, 0xee, 0xff, 0xa7, 0xc1, 0x51,
|
|
||||||
0x91, 0xd9, 0x53, 0xc8, 0x37, 0xf7, 0xc1, 0x0e, 0x4d, 0xc1, 0x9a, 0xe7, 0x9c, 0xfa, 0x51, 0xb0,
|
|
||||||
0xdb, 0x89, 0x39, 0xd0, 0x64, 0xce, 0xdf, 0x55, 0xe6, 0x5c, 0xd4, 0x78, 0x57, 0x39, 0xa7, 0xf7,
|
|
||||||
0x8a, 0x5f, 0x4c, 0xd5, 0xfc, 0x19, 0xe9, 0xfd, 0x02, 0xf6, 0x6b, 0x42, 0x39, 0x30, 0x43, 0x05,
|
|
||||||
0xd6, 0x2d, 0x07, 0x66, 0x95, 0x93, 0xf9, 0x07, 0x9a, 0x23, 0xc6, 0x85, 0xb7, 0x01, 0xe8, 0x09,
|
|
||||||
0xe7, 0x85, 0xa5, 0xcf, 0x5f, 0x5a, 0x52, 0x14, 0x8f, 0x70, 0xae, 0x2c, 0x08, 0x66, 0xef, 0x47,
|
|
||||||
0x30, 0xf6, 0x40, 0x59, 0xb2, 0x51, 0x21, 0xd9, 0x28, 0x4b, 0x9e, 0x41, 0x4b, 0xf5, 0x4b, 0x91,
|
|
||||||
0x0b, 0xf5, 0x28, 0xd8, 0xa5, 0x85, 0x68, 0xb7, 0x4a, 0x94, 0x48, 0xc6, 0xbc, 0xa9, 0x8e, 0xde,
|
|
||||||
0x05, 0x00, 0x00, 0xff, 0xff, 0x75, 0x38, 0xad, 0x84, 0xe4, 0x05, 0x00, 0x00,
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
import "google/protobuf/any.proto";
|
|
||||||
import "testdata/test.proto";
|
|
||||||
|
|
||||||
package proto3_proto;
|
|
||||||
|
|
||||||
message Message {
|
|
||||||
enum Humour {
|
|
||||||
UNKNOWN = 0;
|
|
||||||
PUNS = 1;
|
|
||||||
SLAPSTICK = 2;
|
|
||||||
BILL_BAILEY = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
string name = 1;
|
|
||||||
Humour hilarity = 2;
|
|
||||||
uint32 height_in_cm = 3;
|
|
||||||
bytes data = 4;
|
|
||||||
int64 result_count = 7;
|
|
||||||
bool true_scotsman = 8;
|
|
||||||
float score = 9;
|
|
||||||
|
|
||||||
repeated uint64 key = 5;
|
|
||||||
repeated int32 short_key = 19;
|
|
||||||
Nested nested = 6;
|
|
||||||
repeated Humour r_funny = 16;
|
|
||||||
|
|
||||||
map<string, Nested> terrain = 10;
|
|
||||||
testdata.SubDefaults proto2_field = 11;
|
|
||||||
map<string, testdata.SubDefaults> proto2_value = 13;
|
|
||||||
|
|
||||||
google.protobuf.Any anything = 14;
|
|
||||||
repeated google.protobuf.Any many_things = 15;
|
|
||||||
|
|
||||||
Message submessage = 17;
|
|
||||||
repeated Message children = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Nested {
|
|
||||||
string bunny = 1;
|
|
||||||
bool cute = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MessageWithMap {
|
|
||||||
map<bool, bytes> byte_mapping = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
message IntMap {
|
|
||||||
map<int32, int32> rtt = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message IntMaps {
|
|
||||||
repeated IntMap maps = 1;
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
tpb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProto3ZeroValues(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
m proto.Message
|
|
||||||
}{
|
|
||||||
{"zero message", &pb.Message{}},
|
|
||||||
{"empty bytes field", &pb.Message{Data: []byte{}}},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
b, err := proto.Marshal(test.m)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: proto.Marshal: %v", test.desc, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(b) > 0 {
|
|
||||||
t.Errorf("%s: Encoding is non-empty: %q", test.desc, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRoundTripProto3(t *testing.T) {
|
|
||||||
m := &pb.Message{
|
|
||||||
Name: "David", // (2 | 1<<3): 0x0a 0x05 "David"
|
|
||||||
Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01
|
|
||||||
HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01
|
|
||||||
Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto"
|
|
||||||
ResultCount: 47, // (0 | 7<<3): 0x38 0x2f
|
|
||||||
TrueScotsman: true, // (0 | 8<<3): 0x40 0x01
|
|
||||||
Score: 8.1, // (5 | 9<<3): 0x4d <8.1>
|
|
||||||
|
|
||||||
Key: []uint64{1, 0xdeadbeef},
|
|
||||||
Nested: &pb.Nested{
|
|
||||||
Bunny: "Monty",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
t.Logf(" m: %v", m)
|
|
||||||
|
|
||||||
b, err := proto.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("proto.Marshal: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf(" b: %q", b)
|
|
||||||
|
|
||||||
m2 := new(pb.Message)
|
|
||||||
if err := proto.Unmarshal(b, m2); err != nil {
|
|
||||||
t.Fatalf("proto.Unmarshal: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("m2: %v", m2)
|
|
||||||
|
|
||||||
if !proto.Equal(m, m2) {
|
|
||||||
t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGettersForBasicTypesExist(t *testing.T) {
|
|
||||||
var m pb.Message
|
|
||||||
if got := m.GetNested().GetBunny(); got != "" {
|
|
||||||
t.Errorf("m.GetNested().GetBunny() = %q, want empty string", got)
|
|
||||||
}
|
|
||||||
if got := m.GetNested().GetCute(); got {
|
|
||||||
t.Errorf("m.GetNested().GetCute() = %t, want false", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProto3SetDefaults(t *testing.T) {
|
|
||||||
in := &pb.Message{
|
|
||||||
Terrain: map[string]*pb.Nested{
|
|
||||||
"meadow": new(pb.Nested),
|
|
||||||
},
|
|
||||||
Proto2Field: new(tpb.SubDefaults),
|
|
||||||
Proto2Value: map[string]*tpb.SubDefaults{
|
|
||||||
"badlands": new(tpb.SubDefaults),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
got := proto.Clone(in).(*pb.Message)
|
|
||||||
proto.SetDefaults(got)
|
|
||||||
|
|
||||||
// There are no defaults in proto3. Everything should be the zero value, but
|
|
||||||
// we need to remember to set defaults for nested proto2 messages.
|
|
||||||
want := &pb.Message{
|
|
||||||
Terrain: map[string]*pb.Nested{
|
|
||||||
"meadow": new(pb.Nested),
|
|
||||||
},
|
|
||||||
Proto2Field: &tpb.SubDefaults{N: proto.Int64(7)},
|
|
||||||
Proto2Value: map[string]*tpb.SubDefaults{
|
|
||||||
"badlands": &tpb.SubDefaults{N: proto.Int64(7)},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !proto.Equal(got, want) {
|
|
||||||
t.Errorf("with in = %v\nproto.SetDefaults(in) =>\ngot %v\nwant %v", in, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a separate file and package from size_test.go because that one uses
|
|
||||||
// generated messages and thus may not be in package proto without having a circular
|
|
||||||
// dependency, whereas this file tests unexported details of size.go.
|
|
||||||
|
|
||||||
func TestVarintSize(t *testing.T) {
|
|
||||||
// Check the edge cases carefully.
|
|
||||||
testCases := []struct {
|
|
||||||
n uint64
|
|
||||||
size int
|
|
||||||
}{
|
|
||||||
{0, 1},
|
|
||||||
{1, 1},
|
|
||||||
{127, 1},
|
|
||||||
{128, 2},
|
|
||||||
{16383, 2},
|
|
||||||
{16384, 3},
|
|
||||||
{1<<63 - 1, 9},
|
|
||||||
{1 << 63, 10},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
size := sizeVarint(tc.n)
|
|
||||||
if size != tc.size {
|
|
||||||
t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/golang/protobuf/proto"
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
|
|
||||||
// messageWithExtension2 is in equal_test.go.
|
|
||||||
var messageWithExtension3 = &pb.MyMessage{Count: Int32(8)}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := SetExtension(messageWithExtension1, pb.E_Ext_More, &pb.Ext{Data: String("Abbott")}); err != nil {
|
|
||||||
log.Panicf("SetExtension: %v", err)
|
|
||||||
}
|
|
||||||
if err := SetExtension(messageWithExtension3, pb.E_Ext_More, &pb.Ext{Data: String("Costello")}); err != nil {
|
|
||||||
log.Panicf("SetExtension: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force messageWithExtension3 to have the extension encoded.
|
|
||||||
Marshal(messageWithExtension3)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var SizeTests = []struct {
|
|
||||||
desc string
|
|
||||||
pb Message
|
|
||||||
}{
|
|
||||||
{"empty", &pb.OtherMessage{}},
|
|
||||||
// Basic types.
|
|
||||||
{"bool", &pb.Defaults{F_Bool: Bool(true)}},
|
|
||||||
{"int32", &pb.Defaults{F_Int32: Int32(12)}},
|
|
||||||
{"negative int32", &pb.Defaults{F_Int32: Int32(-1)}},
|
|
||||||
{"small int64", &pb.Defaults{F_Int64: Int64(1)}},
|
|
||||||
{"big int64", &pb.Defaults{F_Int64: Int64(1 << 20)}},
|
|
||||||
{"negative int64", &pb.Defaults{F_Int64: Int64(-1)}},
|
|
||||||
{"fixed32", &pb.Defaults{F_Fixed32: Uint32(71)}},
|
|
||||||
{"fixed64", &pb.Defaults{F_Fixed64: Uint64(72)}},
|
|
||||||
{"uint32", &pb.Defaults{F_Uint32: Uint32(123)}},
|
|
||||||
{"uint64", &pb.Defaults{F_Uint64: Uint64(124)}},
|
|
||||||
{"float", &pb.Defaults{F_Float: Float32(12.6)}},
|
|
||||||
{"double", &pb.Defaults{F_Double: Float64(13.9)}},
|
|
||||||
{"string", &pb.Defaults{F_String: String("niles")}},
|
|
||||||
{"bytes", &pb.Defaults{F_Bytes: []byte("wowsa")}},
|
|
||||||
{"bytes, empty", &pb.Defaults{F_Bytes: []byte{}}},
|
|
||||||
{"sint32", &pb.Defaults{F_Sint32: Int32(65)}},
|
|
||||||
{"sint64", &pb.Defaults{F_Sint64: Int64(67)}},
|
|
||||||
{"enum", &pb.Defaults{F_Enum: pb.Defaults_BLUE.Enum()}},
|
|
||||||
// Repeated.
|
|
||||||
{"empty repeated bool", &pb.MoreRepeated{Bools: []bool{}}},
|
|
||||||
{"repeated bool", &pb.MoreRepeated{Bools: []bool{false, true, true, false}}},
|
|
||||||
{"packed repeated bool", &pb.MoreRepeated{BoolsPacked: []bool{false, true, true, false, true, true, true}}},
|
|
||||||
{"repeated int32", &pb.MoreRepeated{Ints: []int32{1, 12203, 1729, -1}}},
|
|
||||||
{"repeated int32 packed", &pb.MoreRepeated{IntsPacked: []int32{1, 12203, 1729}}},
|
|
||||||
{"repeated int64 packed", &pb.MoreRepeated{Int64SPacked: []int64{
|
|
||||||
// Need enough large numbers to verify that the header is counting the number of bytes
|
|
||||||
// for the field, not the number of elements.
|
|
||||||
1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
|
|
||||||
1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
|
|
||||||
}}},
|
|
||||||
{"repeated string", &pb.MoreRepeated{Strings: []string{"r", "ken", "gri"}}},
|
|
||||||
{"repeated fixed", &pb.MoreRepeated{Fixeds: []uint32{1, 2, 3, 4}}},
|
|
||||||
// Nested.
|
|
||||||
{"nested", &pb.OldMessage{Nested: &pb.OldMessage_Nested{Name: String("whatever")}}},
|
|
||||||
{"group", &pb.GroupOld{G: &pb.GroupOld_G{X: Int32(12345)}}},
|
|
||||||
// Other things.
|
|
||||||
{"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}},
|
|
||||||
{"extension (unencoded)", messageWithExtension1},
|
|
||||||
{"extension (encoded)", messageWithExtension3},
|
|
||||||
// proto3 message
|
|
||||||
{"proto3 empty", &proto3pb.Message{}},
|
|
||||||
{"proto3 bool", &proto3pb.Message{TrueScotsman: true}},
|
|
||||||
{"proto3 int64", &proto3pb.Message{ResultCount: 1}},
|
|
||||||
{"proto3 uint32", &proto3pb.Message{HeightInCm: 123}},
|
|
||||||
{"proto3 float", &proto3pb.Message{Score: 12.6}},
|
|
||||||
{"proto3 string", &proto3pb.Message{Name: "Snezana"}},
|
|
||||||
{"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}},
|
|
||||||
{"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}},
|
|
||||||
{"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
|
|
||||||
{"proto3 map field with empty bytes", &proto3pb.MessageWithMap{ByteMapping: map[bool][]byte{false: []byte{}}}},
|
|
||||||
|
|
||||||
{"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}},
|
|
||||||
{"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: &pb.FloatingPoint{F: Float64(2.0)}}}},
|
|
||||||
{"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}},
|
|
||||||
{"map field with empty bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte{}}}},
|
|
||||||
|
|
||||||
{"map field with big entry", &pb.MessageWithMap{NameMapping: map[int32]string{8: strings.Repeat("x", 125)}}},
|
|
||||||
{"map field with big key and val", &pb.MessageWithMap{StrToStr: map[string]string{strings.Repeat("x", 70): strings.Repeat("y", 70)}}},
|
|
||||||
{"map field with big numeric key", &pb.MessageWithMap{NameMapping: map[int32]string{0xf00d: "om nom nom"}}},
|
|
||||||
|
|
||||||
{"oneof not set", &pb.Oneof{}},
|
|
||||||
{"oneof bool", &pb.Oneof{Union: &pb.Oneof_F_Bool{true}}},
|
|
||||||
{"oneof zero int32", &pb.Oneof{Union: &pb.Oneof_F_Int32{0}}},
|
|
||||||
{"oneof big int32", &pb.Oneof{Union: &pb.Oneof_F_Int32{1 << 20}}},
|
|
||||||
{"oneof int64", &pb.Oneof{Union: &pb.Oneof_F_Int64{42}}},
|
|
||||||
{"oneof fixed32", &pb.Oneof{Union: &pb.Oneof_F_Fixed32{43}}},
|
|
||||||
{"oneof fixed64", &pb.Oneof{Union: &pb.Oneof_F_Fixed64{44}}},
|
|
||||||
{"oneof uint32", &pb.Oneof{Union: &pb.Oneof_F_Uint32{45}}},
|
|
||||||
{"oneof uint64", &pb.Oneof{Union: &pb.Oneof_F_Uint64{46}}},
|
|
||||||
{"oneof float", &pb.Oneof{Union: &pb.Oneof_F_Float{47.1}}},
|
|
||||||
{"oneof double", &pb.Oneof{Union: &pb.Oneof_F_Double{48.9}}},
|
|
||||||
{"oneof string", &pb.Oneof{Union: &pb.Oneof_F_String{"Rhythmic Fman"}}},
|
|
||||||
{"oneof bytes", &pb.Oneof{Union: &pb.Oneof_F_Bytes{[]byte("let go")}}},
|
|
||||||
{"oneof sint32", &pb.Oneof{Union: &pb.Oneof_F_Sint32{50}}},
|
|
||||||
{"oneof sint64", &pb.Oneof{Union: &pb.Oneof_F_Sint64{51}}},
|
|
||||||
{"oneof enum", &pb.Oneof{Union: &pb.Oneof_F_Enum{pb.MyMessage_BLUE}}},
|
|
||||||
{"message for oneof", &pb.GoTestField{Label: String("k"), Type: String("v")}},
|
|
||||||
{"oneof message", &pb.Oneof{Union: &pb.Oneof_F_Message{&pb.GoTestField{Label: String("k"), Type: String("v")}}}},
|
|
||||||
{"oneof group", &pb.Oneof{Union: &pb.Oneof_FGroup{&pb.Oneof_F_Group{X: Int32(52)}}}},
|
|
||||||
{"oneof largest tag", &pb.Oneof{Union: &pb.Oneof_F_Largest_Tag{1}}},
|
|
||||||
{"multiple oneofs", &pb.Oneof{Union: &pb.Oneof_F_Int32{1}, Tormato: &pb.Oneof_Value{2}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSize(t *testing.T) {
|
|
||||||
for _, tc := range SizeTests {
|
|
||||||
size := Size(tc.pb)
|
|
||||||
b, err := Marshal(tc.pb)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v: Marshal failed: %v", tc.desc, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if size != len(b) {
|
|
||||||
t.Errorf("%v: Size(%v) = %d, want %d", tc.desc, tc.pb, size, len(b))
|
|
||||||
t.Logf("%v: bytes: %#v", tc.desc, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
# Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
#
|
|
||||||
# Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
# https://github.com/golang/protobuf
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are
|
|
||||||
# met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# * Redistributions in binary form must reproduce the above
|
|
||||||
# copyright notice, this list of conditions and the following disclaimer
|
|
||||||
# in the documentation and/or other materials provided with the
|
|
||||||
# distribution.
|
|
||||||
# * Neither the name of Google Inc. nor the names of its
|
|
||||||
# contributors may be used to endorse or promote products derived from
|
|
||||||
# this software without specific prior written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
include ../../Make.protobuf
|
|
||||||
|
|
||||||
all: regenerate
|
|
||||||
|
|
||||||
regenerate:
|
|
||||||
rm -f test.pb.go
|
|
||||||
make test.pb.go
|
|
||||||
|
|
||||||
# The following rules are just aids to development. Not needed for typical testing.
|
|
||||||
|
|
||||||
diff: regenerate
|
|
||||||
git diff test.pb.go
|
|
||||||
|
|
||||||
restore:
|
|
||||||
cp test.pb.go.golden test.pb.go
|
|
||||||
|
|
||||||
preserve:
|
|
||||||
cp test.pb.go test.pb.go.golden
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// Verify that the compiler output for test.proto is unchanged.
|
|
||||||
|
|
||||||
package testdata
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// sum returns in string form (for easy comparison) the SHA-1 hash of the named file.
|
|
||||||
func sum(t *testing.T, name string) string {
|
|
||||||
data, err := ioutil.ReadFile(name)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Logf("sum(%q): length is %d", name, len(data))
|
|
||||||
hash := sha1.New()
|
|
||||||
_, err = hash.Write(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("% x", hash.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(t *testing.T, name string, args ...string) {
|
|
||||||
cmd := exec.Command(name, args...)
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGolden(t *testing.T) {
|
|
||||||
// Compute the original checksum.
|
|
||||||
goldenSum := sum(t, "test.pb.go")
|
|
||||||
// Run the proto compiler.
|
|
||||||
run(t, "protoc", "--go_out="+os.TempDir(), "test.proto")
|
|
||||||
newFile := filepath.Join(os.TempDir(), "test.pb.go")
|
|
||||||
defer os.Remove(newFile)
|
|
||||||
// Compute the new checksum.
|
|
||||||
newSum := sum(t, newFile)
|
|
||||||
// Verify
|
|
||||||
if newSum != goldenSum {
|
|
||||||
run(t, "diff", "-u", "test.pb.go", newFile)
|
|
||||||
t.Fatal("Code generated by protoc-gen-go has changed; update test.pb.go")
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,548 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// A feature-rich test file for the protocol compiler and libraries.
|
|
||||||
|
|
||||||
syntax = "proto2";
|
|
||||||
|
|
||||||
package testdata;
|
|
||||||
|
|
||||||
enum FOO { FOO1 = 1; };
|
|
||||||
|
|
||||||
message GoEnum {
|
|
||||||
required FOO foo = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GoTestField {
|
|
||||||
required string Label = 1;
|
|
||||||
required string Type = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GoTest {
|
|
||||||
// An enum, for completeness.
|
|
||||||
enum KIND {
|
|
||||||
VOID = 0;
|
|
||||||
|
|
||||||
// Basic types
|
|
||||||
BOOL = 1;
|
|
||||||
BYTES = 2;
|
|
||||||
FINGERPRINT = 3;
|
|
||||||
FLOAT = 4;
|
|
||||||
INT = 5;
|
|
||||||
STRING = 6;
|
|
||||||
TIME = 7;
|
|
||||||
|
|
||||||
// Groupings
|
|
||||||
TUPLE = 8;
|
|
||||||
ARRAY = 9;
|
|
||||||
MAP = 10;
|
|
||||||
|
|
||||||
// Table types
|
|
||||||
TABLE = 11;
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
FUNCTION = 12; // last tag
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some typical parameters
|
|
||||||
required KIND Kind = 1;
|
|
||||||
optional string Table = 2;
|
|
||||||
optional int32 Param = 3;
|
|
||||||
|
|
||||||
// Required, repeated and optional foreign fields.
|
|
||||||
required GoTestField RequiredField = 4;
|
|
||||||
repeated GoTestField RepeatedField = 5;
|
|
||||||
optional GoTestField OptionalField = 6;
|
|
||||||
|
|
||||||
// Required fields of all basic types
|
|
||||||
required bool F_Bool_required = 10;
|
|
||||||
required int32 F_Int32_required = 11;
|
|
||||||
required int64 F_Int64_required = 12;
|
|
||||||
required fixed32 F_Fixed32_required = 13;
|
|
||||||
required fixed64 F_Fixed64_required = 14;
|
|
||||||
required uint32 F_Uint32_required = 15;
|
|
||||||
required uint64 F_Uint64_required = 16;
|
|
||||||
required float F_Float_required = 17;
|
|
||||||
required double F_Double_required = 18;
|
|
||||||
required string F_String_required = 19;
|
|
||||||
required bytes F_Bytes_required = 101;
|
|
||||||
required sint32 F_Sint32_required = 102;
|
|
||||||
required sint64 F_Sint64_required = 103;
|
|
||||||
|
|
||||||
// Repeated fields of all basic types
|
|
||||||
repeated bool F_Bool_repeated = 20;
|
|
||||||
repeated int32 F_Int32_repeated = 21;
|
|
||||||
repeated int64 F_Int64_repeated = 22;
|
|
||||||
repeated fixed32 F_Fixed32_repeated = 23;
|
|
||||||
repeated fixed64 F_Fixed64_repeated = 24;
|
|
||||||
repeated uint32 F_Uint32_repeated = 25;
|
|
||||||
repeated uint64 F_Uint64_repeated = 26;
|
|
||||||
repeated float F_Float_repeated = 27;
|
|
||||||
repeated double F_Double_repeated = 28;
|
|
||||||
repeated string F_String_repeated = 29;
|
|
||||||
repeated bytes F_Bytes_repeated = 201;
|
|
||||||
repeated sint32 F_Sint32_repeated = 202;
|
|
||||||
repeated sint64 F_Sint64_repeated = 203;
|
|
||||||
|
|
||||||
// Optional fields of all basic types
|
|
||||||
optional bool F_Bool_optional = 30;
|
|
||||||
optional int32 F_Int32_optional = 31;
|
|
||||||
optional int64 F_Int64_optional = 32;
|
|
||||||
optional fixed32 F_Fixed32_optional = 33;
|
|
||||||
optional fixed64 F_Fixed64_optional = 34;
|
|
||||||
optional uint32 F_Uint32_optional = 35;
|
|
||||||
optional uint64 F_Uint64_optional = 36;
|
|
||||||
optional float F_Float_optional = 37;
|
|
||||||
optional double F_Double_optional = 38;
|
|
||||||
optional string F_String_optional = 39;
|
|
||||||
optional bytes F_Bytes_optional = 301;
|
|
||||||
optional sint32 F_Sint32_optional = 302;
|
|
||||||
optional sint64 F_Sint64_optional = 303;
|
|
||||||
|
|
||||||
// Default-valued fields of all basic types
|
|
||||||
optional bool F_Bool_defaulted = 40 [default=true];
|
|
||||||
optional int32 F_Int32_defaulted = 41 [default=32];
|
|
||||||
optional int64 F_Int64_defaulted = 42 [default=64];
|
|
||||||
optional fixed32 F_Fixed32_defaulted = 43 [default=320];
|
|
||||||
optional fixed64 F_Fixed64_defaulted = 44 [default=640];
|
|
||||||
optional uint32 F_Uint32_defaulted = 45 [default=3200];
|
|
||||||
optional uint64 F_Uint64_defaulted = 46 [default=6400];
|
|
||||||
optional float F_Float_defaulted = 47 [default=314159.];
|
|
||||||
optional double F_Double_defaulted = 48 [default=271828.];
|
|
||||||
optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"];
|
|
||||||
optional bytes F_Bytes_defaulted = 401 [default="Bignose"];
|
|
||||||
optional sint32 F_Sint32_defaulted = 402 [default = -32];
|
|
||||||
optional sint64 F_Sint64_defaulted = 403 [default = -64];
|
|
||||||
|
|
||||||
// Packed repeated fields (no string or bytes).
|
|
||||||
repeated bool F_Bool_repeated_packed = 50 [packed=true];
|
|
||||||
repeated int32 F_Int32_repeated_packed = 51 [packed=true];
|
|
||||||
repeated int64 F_Int64_repeated_packed = 52 [packed=true];
|
|
||||||
repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true];
|
|
||||||
repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true];
|
|
||||||
repeated uint32 F_Uint32_repeated_packed = 55 [packed=true];
|
|
||||||
repeated uint64 F_Uint64_repeated_packed = 56 [packed=true];
|
|
||||||
repeated float F_Float_repeated_packed = 57 [packed=true];
|
|
||||||
repeated double F_Double_repeated_packed = 58 [packed=true];
|
|
||||||
repeated sint32 F_Sint32_repeated_packed = 502 [packed=true];
|
|
||||||
repeated sint64 F_Sint64_repeated_packed = 503 [packed=true];
|
|
||||||
|
|
||||||
// Required, repeated, and optional groups.
|
|
||||||
required group RequiredGroup = 70 {
|
|
||||||
required string RequiredField = 71;
|
|
||||||
};
|
|
||||||
|
|
||||||
repeated group RepeatedGroup = 80 {
|
|
||||||
required string RequiredField = 81;
|
|
||||||
};
|
|
||||||
|
|
||||||
optional group OptionalGroup = 90 {
|
|
||||||
required string RequiredField = 91;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// For testing a group containing a required field.
|
|
||||||
message GoTestRequiredGroupField {
|
|
||||||
required group Group = 1 {
|
|
||||||
required int32 Field = 2;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// For testing skipping of unrecognized fields.
|
|
||||||
// Numbers are all big, larger than tag numbers in GoTestField,
|
|
||||||
// the message used in the corresponding test.
|
|
||||||
message GoSkipTest {
|
|
||||||
required int32 skip_int32 = 11;
|
|
||||||
required fixed32 skip_fixed32 = 12;
|
|
||||||
required fixed64 skip_fixed64 = 13;
|
|
||||||
required string skip_string = 14;
|
|
||||||
required group SkipGroup = 15 {
|
|
||||||
required int32 group_int32 = 16;
|
|
||||||
required string group_string = 17;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For testing packed/non-packed decoder switching.
|
|
||||||
// A serialized instance of one should be deserializable as the other.
|
|
||||||
message NonPackedTest {
|
|
||||||
repeated int32 a = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PackedTest {
|
|
||||||
repeated int32 b = 1 [packed=true];
|
|
||||||
}
|
|
||||||
|
|
||||||
message MaxTag {
|
|
||||||
// Maximum possible tag number.
|
|
||||||
optional string last_field = 536870911;
|
|
||||||
}
|
|
||||||
|
|
||||||
message OldMessage {
|
|
||||||
message Nested {
|
|
||||||
optional string name = 1;
|
|
||||||
}
|
|
||||||
optional Nested nested = 1;
|
|
||||||
|
|
||||||
optional int32 num = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMessage is wire compatible with OldMessage;
|
|
||||||
// imagine it as a future version.
|
|
||||||
message NewMessage {
|
|
||||||
message Nested {
|
|
||||||
optional string name = 1;
|
|
||||||
optional string food_group = 2;
|
|
||||||
}
|
|
||||||
optional Nested nested = 1;
|
|
||||||
|
|
||||||
// This is an int32 in OldMessage.
|
|
||||||
optional int64 num = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smaller tests for ASCII formatting.
|
|
||||||
|
|
||||||
message InnerMessage {
|
|
||||||
required string host = 1;
|
|
||||||
optional int32 port = 2 [default=4000];
|
|
||||||
optional bool connected = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message OtherMessage {
|
|
||||||
optional int64 key = 1;
|
|
||||||
optional bytes value = 2;
|
|
||||||
optional float weight = 3;
|
|
||||||
optional InnerMessage inner = 4;
|
|
||||||
|
|
||||||
extensions 100 to max;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RequiredInnerMessage {
|
|
||||||
required InnerMessage leo_finally_won_an_oscar = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MyMessage {
|
|
||||||
required int32 count = 1;
|
|
||||||
optional string name = 2;
|
|
||||||
optional string quote = 3;
|
|
||||||
repeated string pet = 4;
|
|
||||||
optional InnerMessage inner = 5;
|
|
||||||
repeated OtherMessage others = 6;
|
|
||||||
optional RequiredInnerMessage we_must_go_deeper = 13;
|
|
||||||
repeated InnerMessage rep_inner = 12;
|
|
||||||
|
|
||||||
enum Color {
|
|
||||||
RED = 0;
|
|
||||||
GREEN = 1;
|
|
||||||
BLUE = 2;
|
|
||||||
};
|
|
||||||
optional Color bikeshed = 7;
|
|
||||||
|
|
||||||
optional group SomeGroup = 8 {
|
|
||||||
optional int32 group_field = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This field becomes [][]byte in the generated code.
|
|
||||||
repeated bytes rep_bytes = 10;
|
|
||||||
|
|
||||||
optional double bigfloat = 11;
|
|
||||||
|
|
||||||
extensions 100 to max;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Ext {
|
|
||||||
extend MyMessage {
|
|
||||||
optional Ext more = 103;
|
|
||||||
optional string text = 104;
|
|
||||||
optional int32 number = 105;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string data = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
extend MyMessage {
|
|
||||||
repeated string greeting = 106;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ComplexExtension {
|
|
||||||
optional int32 first = 1;
|
|
||||||
optional int32 second = 2;
|
|
||||||
repeated int32 third = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
extend OtherMessage {
|
|
||||||
optional ComplexExtension complex = 200;
|
|
||||||
repeated ComplexExtension r_complex = 201;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DefaultsMessage {
|
|
||||||
enum DefaultsEnum {
|
|
||||||
ZERO = 0;
|
|
||||||
ONE = 1;
|
|
||||||
TWO = 2;
|
|
||||||
};
|
|
||||||
extensions 100 to max;
|
|
||||||
}
|
|
||||||
|
|
||||||
extend DefaultsMessage {
|
|
||||||
optional double no_default_double = 101;
|
|
||||||
optional float no_default_float = 102;
|
|
||||||
optional int32 no_default_int32 = 103;
|
|
||||||
optional int64 no_default_int64 = 104;
|
|
||||||
optional uint32 no_default_uint32 = 105;
|
|
||||||
optional uint64 no_default_uint64 = 106;
|
|
||||||
optional sint32 no_default_sint32 = 107;
|
|
||||||
optional sint64 no_default_sint64 = 108;
|
|
||||||
optional fixed32 no_default_fixed32 = 109;
|
|
||||||
optional fixed64 no_default_fixed64 = 110;
|
|
||||||
optional sfixed32 no_default_sfixed32 = 111;
|
|
||||||
optional sfixed64 no_default_sfixed64 = 112;
|
|
||||||
optional bool no_default_bool = 113;
|
|
||||||
optional string no_default_string = 114;
|
|
||||||
optional bytes no_default_bytes = 115;
|
|
||||||
optional DefaultsMessage.DefaultsEnum no_default_enum = 116;
|
|
||||||
|
|
||||||
optional double default_double = 201 [default = 3.1415];
|
|
||||||
optional float default_float = 202 [default = 3.14];
|
|
||||||
optional int32 default_int32 = 203 [default = 42];
|
|
||||||
optional int64 default_int64 = 204 [default = 43];
|
|
||||||
optional uint32 default_uint32 = 205 [default = 44];
|
|
||||||
optional uint64 default_uint64 = 206 [default = 45];
|
|
||||||
optional sint32 default_sint32 = 207 [default = 46];
|
|
||||||
optional sint64 default_sint64 = 208 [default = 47];
|
|
||||||
optional fixed32 default_fixed32 = 209 [default = 48];
|
|
||||||
optional fixed64 default_fixed64 = 210 [default = 49];
|
|
||||||
optional sfixed32 default_sfixed32 = 211 [default = 50];
|
|
||||||
optional sfixed64 default_sfixed64 = 212 [default = 51];
|
|
||||||
optional bool default_bool = 213 [default = true];
|
|
||||||
optional string default_string = 214 [default = "Hello, string"];
|
|
||||||
optional bytes default_bytes = 215 [default = "Hello, bytes"];
|
|
||||||
optional DefaultsMessage.DefaultsEnum default_enum = 216 [default = ONE];
|
|
||||||
}
|
|
||||||
|
|
||||||
message MyMessageSet {
|
|
||||||
option message_set_wire_format = true;
|
|
||||||
extensions 100 to max;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Empty {
|
|
||||||
}
|
|
||||||
|
|
||||||
extend MyMessageSet {
|
|
||||||
optional Empty x201 = 201;
|
|
||||||
optional Empty x202 = 202;
|
|
||||||
optional Empty x203 = 203;
|
|
||||||
optional Empty x204 = 204;
|
|
||||||
optional Empty x205 = 205;
|
|
||||||
optional Empty x206 = 206;
|
|
||||||
optional Empty x207 = 207;
|
|
||||||
optional Empty x208 = 208;
|
|
||||||
optional Empty x209 = 209;
|
|
||||||
optional Empty x210 = 210;
|
|
||||||
optional Empty x211 = 211;
|
|
||||||
optional Empty x212 = 212;
|
|
||||||
optional Empty x213 = 213;
|
|
||||||
optional Empty x214 = 214;
|
|
||||||
optional Empty x215 = 215;
|
|
||||||
optional Empty x216 = 216;
|
|
||||||
optional Empty x217 = 217;
|
|
||||||
optional Empty x218 = 218;
|
|
||||||
optional Empty x219 = 219;
|
|
||||||
optional Empty x220 = 220;
|
|
||||||
optional Empty x221 = 221;
|
|
||||||
optional Empty x222 = 222;
|
|
||||||
optional Empty x223 = 223;
|
|
||||||
optional Empty x224 = 224;
|
|
||||||
optional Empty x225 = 225;
|
|
||||||
optional Empty x226 = 226;
|
|
||||||
optional Empty x227 = 227;
|
|
||||||
optional Empty x228 = 228;
|
|
||||||
optional Empty x229 = 229;
|
|
||||||
optional Empty x230 = 230;
|
|
||||||
optional Empty x231 = 231;
|
|
||||||
optional Empty x232 = 232;
|
|
||||||
optional Empty x233 = 233;
|
|
||||||
optional Empty x234 = 234;
|
|
||||||
optional Empty x235 = 235;
|
|
||||||
optional Empty x236 = 236;
|
|
||||||
optional Empty x237 = 237;
|
|
||||||
optional Empty x238 = 238;
|
|
||||||
optional Empty x239 = 239;
|
|
||||||
optional Empty x240 = 240;
|
|
||||||
optional Empty x241 = 241;
|
|
||||||
optional Empty x242 = 242;
|
|
||||||
optional Empty x243 = 243;
|
|
||||||
optional Empty x244 = 244;
|
|
||||||
optional Empty x245 = 245;
|
|
||||||
optional Empty x246 = 246;
|
|
||||||
optional Empty x247 = 247;
|
|
||||||
optional Empty x248 = 248;
|
|
||||||
optional Empty x249 = 249;
|
|
||||||
optional Empty x250 = 250;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MessageList {
|
|
||||||
repeated group Message = 1 {
|
|
||||||
required string name = 2;
|
|
||||||
required int32 count = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message Strings {
|
|
||||||
optional string string_field = 1;
|
|
||||||
optional bytes bytes_field = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Defaults {
|
|
||||||
enum Color {
|
|
||||||
RED = 0;
|
|
||||||
GREEN = 1;
|
|
||||||
BLUE = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default-valued fields of all basic types.
|
|
||||||
// Same as GoTest, but copied here to make testing easier.
|
|
||||||
optional bool F_Bool = 1 [default=true];
|
|
||||||
optional int32 F_Int32 = 2 [default=32];
|
|
||||||
optional int64 F_Int64 = 3 [default=64];
|
|
||||||
optional fixed32 F_Fixed32 = 4 [default=320];
|
|
||||||
optional fixed64 F_Fixed64 = 5 [default=640];
|
|
||||||
optional uint32 F_Uint32 = 6 [default=3200];
|
|
||||||
optional uint64 F_Uint64 = 7 [default=6400];
|
|
||||||
optional float F_Float = 8 [default=314159.];
|
|
||||||
optional double F_Double = 9 [default=271828.];
|
|
||||||
optional string F_String = 10 [default="hello, \"world!\"\n"];
|
|
||||||
optional bytes F_Bytes = 11 [default="Bignose"];
|
|
||||||
optional sint32 F_Sint32 = 12 [default=-32];
|
|
||||||
optional sint64 F_Sint64 = 13 [default=-64];
|
|
||||||
optional Color F_Enum = 14 [default=GREEN];
|
|
||||||
|
|
||||||
// More fields with crazy defaults.
|
|
||||||
optional float F_Pinf = 15 [default=inf];
|
|
||||||
optional float F_Ninf = 16 [default=-inf];
|
|
||||||
optional float F_Nan = 17 [default=nan];
|
|
||||||
|
|
||||||
// Sub-message.
|
|
||||||
optional SubDefaults sub = 18;
|
|
||||||
|
|
||||||
// Redundant but explicit defaults.
|
|
||||||
optional string str_zero = 19 [default=""];
|
|
||||||
}
|
|
||||||
|
|
||||||
message SubDefaults {
|
|
||||||
optional int64 n = 1 [default=7];
|
|
||||||
}
|
|
||||||
|
|
||||||
message RepeatedEnum {
|
|
||||||
enum Color {
|
|
||||||
RED = 1;
|
|
||||||
}
|
|
||||||
repeated Color color = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MoreRepeated {
|
|
||||||
repeated bool bools = 1;
|
|
||||||
repeated bool bools_packed = 2 [packed=true];
|
|
||||||
repeated int32 ints = 3;
|
|
||||||
repeated int32 ints_packed = 4 [packed=true];
|
|
||||||
repeated int64 int64s_packed = 7 [packed=true];
|
|
||||||
repeated string strings = 5;
|
|
||||||
repeated fixed32 fixeds = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupOld and GroupNew have the same wire format.
|
|
||||||
// GroupNew has a new field inside a group.
|
|
||||||
|
|
||||||
message GroupOld {
|
|
||||||
optional group G = 101 {
|
|
||||||
optional int32 x = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message GroupNew {
|
|
||||||
optional group G = 101 {
|
|
||||||
optional int32 x = 2;
|
|
||||||
optional int32 y = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message FloatingPoint {
|
|
||||||
required double f = 1;
|
|
||||||
optional bool exact = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MessageWithMap {
|
|
||||||
map<int32, string> name_mapping = 1;
|
|
||||||
map<sint64, FloatingPoint> msg_mapping = 2;
|
|
||||||
map<bool, bytes> byte_mapping = 3;
|
|
||||||
map<string, string> str_to_str = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Oneof {
|
|
||||||
oneof union {
|
|
||||||
bool F_Bool = 1;
|
|
||||||
int32 F_Int32 = 2;
|
|
||||||
int64 F_Int64 = 3;
|
|
||||||
fixed32 F_Fixed32 = 4;
|
|
||||||
fixed64 F_Fixed64 = 5;
|
|
||||||
uint32 F_Uint32 = 6;
|
|
||||||
uint64 F_Uint64 = 7;
|
|
||||||
float F_Float = 8;
|
|
||||||
double F_Double = 9;
|
|
||||||
string F_String = 10;
|
|
||||||
bytes F_Bytes = 11;
|
|
||||||
sint32 F_Sint32 = 12;
|
|
||||||
sint64 F_Sint64 = 13;
|
|
||||||
MyMessage.Color F_Enum = 14;
|
|
||||||
GoTestField F_Message = 15;
|
|
||||||
group F_Group = 16 {
|
|
||||||
optional int32 x = 17;
|
|
||||||
}
|
|
||||||
int32 F_Largest_Tag = 536870911;
|
|
||||||
}
|
|
||||||
|
|
||||||
oneof tormato {
|
|
||||||
int32 value = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message Communique {
|
|
||||||
optional bool make_me_cry = 1;
|
|
||||||
|
|
||||||
// This is a oneof, called "union".
|
|
||||||
oneof union {
|
|
||||||
int32 number = 5;
|
|
||||||
string name = 6;
|
|
||||||
bytes data = 7;
|
|
||||||
double temp_c = 8;
|
|
||||||
MyMessage.Color col = 9;
|
|
||||||
Strings msg = 10;
|
|
||||||
}
|
|
||||||
}
|
|
854
vendor/src/github.com/golang/protobuf/proto/text.go
vendored
854
vendor/src/github.com/golang/protobuf/proto/text.go
vendored
|
@ -1,854 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
// Functions for writing the text protocol buffer format.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
newline = []byte("\n")
|
|
||||||
spaces = []byte(" ")
|
|
||||||
gtNewline = []byte(">\n")
|
|
||||||
endBraceNewline = []byte("}\n")
|
|
||||||
backslashN = []byte{'\\', 'n'}
|
|
||||||
backslashR = []byte{'\\', 'r'}
|
|
||||||
backslashT = []byte{'\\', 't'}
|
|
||||||
backslashDQ = []byte{'\\', '"'}
|
|
||||||
backslashBS = []byte{'\\', '\\'}
|
|
||||||
posInf = []byte("inf")
|
|
||||||
negInf = []byte("-inf")
|
|
||||||
nan = []byte("nan")
|
|
||||||
)
|
|
||||||
|
|
||||||
type writer interface {
|
|
||||||
io.Writer
|
|
||||||
WriteByte(byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// textWriter is an io.Writer that tracks its indentation level.
|
|
||||||
type textWriter struct {
|
|
||||||
ind int
|
|
||||||
complete bool // if the current position is a complete line
|
|
||||||
compact bool // whether to write out as a one-liner
|
|
||||||
w writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) WriteString(s string) (n int, err error) {
|
|
||||||
if !strings.Contains(s, "\n") {
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
w.complete = false
|
|
||||||
return io.WriteString(w.w, s)
|
|
||||||
}
|
|
||||||
// WriteString is typically called without newlines, so this
|
|
||||||
// codepath and its copy are rare. We copy to avoid
|
|
||||||
// duplicating all of Write's logic here.
|
|
||||||
return w.Write([]byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) Write(p []byte) (n int, err error) {
|
|
||||||
newlines := bytes.Count(p, newline)
|
|
||||||
if newlines == 0 {
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
n, err = w.w.Write(p)
|
|
||||||
w.complete = false
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
frags := bytes.SplitN(p, newline, newlines+1)
|
|
||||||
if w.compact {
|
|
||||||
for i, frag := range frags {
|
|
||||||
if i > 0 {
|
|
||||||
if err := w.w.WriteByte(' '); err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
nn, err := w.w.Write(frag)
|
|
||||||
n += nn
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, frag := range frags {
|
|
||||||
if w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
nn, err := w.w.Write(frag)
|
|
||||||
n += nn
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
if i+1 < len(frags) {
|
|
||||||
if err := w.w.WriteByte('\n'); err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.complete = len(frags[len(frags)-1]) == 0
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) WriteByte(c byte) error {
|
|
||||||
if w.compact && c == '\n' {
|
|
||||||
c = ' '
|
|
||||||
}
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
err := w.w.WriteByte(c)
|
|
||||||
w.complete = c == '\n'
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) indent() { w.ind++ }
|
|
||||||
|
|
||||||
func (w *textWriter) unindent() {
|
|
||||||
if w.ind == 0 {
|
|
||||||
log.Print("proto: textWriter unindented too far")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.ind--
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeName(w *textWriter, props *Properties) error {
|
|
||||||
if _, err := w.WriteString(props.OrigName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if props.Wire != "group" {
|
|
||||||
return w.WriteByte(':')
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// raw is the interface satisfied by RawMessage.
|
|
||||||
type raw interface {
|
|
||||||
Bytes() []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiresQuotes(u string) bool {
|
|
||||||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
|
||||||
for _, ch := range u {
|
|
||||||
switch {
|
|
||||||
case ch == '.' || ch == '/' || ch == '_':
|
|
||||||
continue
|
|
||||||
case '0' <= ch && ch <= '9':
|
|
||||||
continue
|
|
||||||
case 'A' <= ch && ch <= 'Z':
|
|
||||||
continue
|
|
||||||
case 'a' <= ch && ch <= 'z':
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isAny reports whether sv is a google.protobuf.Any message
|
|
||||||
func isAny(sv reflect.Value) bool {
|
|
||||||
type wkt interface {
|
|
||||||
XXX_WellKnownType() string
|
|
||||||
}
|
|
||||||
t, ok := sv.Addr().Interface().(wkt)
|
|
||||||
return ok && t.XXX_WellKnownType() == "Any"
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeProto3Any writes an expanded google.protobuf.Any message.
|
|
||||||
//
|
|
||||||
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
|
|
||||||
// required messages are not linked in).
|
|
||||||
//
|
|
||||||
// It returns (true, error) when sv was written in expanded format or an error
|
|
||||||
// was encountered.
|
|
||||||
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
|
||||||
turl := sv.FieldByName("TypeUrl")
|
|
||||||
val := sv.FieldByName("Value")
|
|
||||||
if !turl.IsValid() || !val.IsValid() {
|
|
||||||
return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
||||||
}
|
|
||||||
|
|
||||||
b, ok := val.Interface().([]byte)
|
|
||||||
if !ok {
|
|
||||||
return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.Split(turl.String(), "/")
|
|
||||||
mt := MessageType(parts[len(parts)-1])
|
|
||||||
if mt == nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
m := reflect.New(mt.Elem())
|
|
||||||
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
w.Write([]byte("["))
|
|
||||||
u := turl.String()
|
|
||||||
if requiresQuotes(u) {
|
|
||||||
writeString(w, u)
|
|
||||||
} else {
|
|
||||||
w.Write([]byte(u))
|
|
||||||
}
|
|
||||||
if w.compact {
|
|
||||||
w.Write([]byte("]:<"))
|
|
||||||
} else {
|
|
||||||
w.Write([]byte("]: <\n"))
|
|
||||||
w.ind++
|
|
||||||
}
|
|
||||||
if err := tm.writeStruct(w, m.Elem()); err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
if w.compact {
|
|
||||||
w.Write([]byte("> "))
|
|
||||||
} else {
|
|
||||||
w.ind--
|
|
||||||
w.Write([]byte(">\n"))
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|
||||||
if tm.ExpandAny && isAny(sv) {
|
|
||||||
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
st := sv.Type()
|
|
||||||
sprops := GetProperties(st)
|
|
||||||
for i := 0; i < sv.NumField(); i++ {
|
|
||||||
fv := sv.Field(i)
|
|
||||||
props := sprops.Prop[i]
|
|
||||||
name := st.Field(i).Name
|
|
||||||
|
|
||||||
if strings.HasPrefix(name, "XXX_") {
|
|
||||||
// There are two XXX_ fields:
|
|
||||||
// XXX_unrecognized []byte
|
|
||||||
// XXX_extensions map[int32]proto.Extension
|
|
||||||
// The first is handled here;
|
|
||||||
// the second is handled at the bottom of this function.
|
|
||||||
if name == "XXX_unrecognized" && !fv.IsNil() {
|
|
||||||
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
|
||||||
// Field not filled in. This could be an optional field or
|
|
||||||
// a required field that wasn't filled in. Either way, there
|
|
||||||
// isn't anything we can show for it.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
|
||||||
// Repeated field that is empty, or a bytes field that is unused.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if props.Repeated && fv.Kind() == reflect.Slice {
|
|
||||||
// Repeated field.
|
|
||||||
for j := 0; j < fv.Len(); j++ {
|
|
||||||
if err := writeName(w, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v := fv.Index(j)
|
|
||||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
|
||||||
// A nil message in a repeated field is not valid,
|
|
||||||
// but we can handle that more gracefully than panicking.
|
|
||||||
if _, err := w.Write([]byte("<nil>\n")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, v, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() == reflect.Map {
|
|
||||||
// Map fields are rendered as a repeated struct with key/value fields.
|
|
||||||
keys := fv.MapKeys()
|
|
||||||
sort.Sort(mapKeys(keys))
|
|
||||||
for _, key := range keys {
|
|
||||||
val := fv.MapIndex(key)
|
|
||||||
if err := writeName(w, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// open struct
|
|
||||||
if err := w.WriteByte('<'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.indent()
|
|
||||||
// key
|
|
||||||
if _, err := w.WriteString("key:"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// nil values aren't legal, but we can avoid panicking because of them.
|
|
||||||
if val.Kind() != reflect.Ptr || !val.IsNil() {
|
|
||||||
// value
|
|
||||||
if _, err := w.WriteString("value:"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// close struct
|
|
||||||
w.unindent()
|
|
||||||
if err := w.WriteByte('>'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
|
|
||||||
// empty bytes field
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
|
|
||||||
// proto3 non-repeated scalar field; skip if zero value
|
|
||||||
if isProto3Zero(fv) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fv.Kind() == reflect.Interface {
|
|
||||||
// Check if it is a oneof.
|
|
||||||
if st.Field(i).Tag.Get("protobuf_oneof") != "" {
|
|
||||||
// fv is nil, or holds a pointer to generated struct.
|
|
||||||
// That generated struct has exactly one field,
|
|
||||||
// which has a protobuf struct tag.
|
|
||||||
if fv.IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
inner := fv.Elem().Elem() // interface -> *T -> T
|
|
||||||
tag := inner.Type().Field(0).Tag.Get("protobuf")
|
|
||||||
props = new(Properties) // Overwrite the outer props var, but not its pointee.
|
|
||||||
props.Parse(tag)
|
|
||||||
// Write the value in the oneof, not the oneof itself.
|
|
||||||
fv = inner.Field(0)
|
|
||||||
|
|
||||||
// Special case to cope with malformed messages gracefully:
|
|
||||||
// If the value in the oneof is a nil pointer, don't panic
|
|
||||||
// in writeAny.
|
|
||||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
|
||||||
// Use errors.New so writeAny won't render quotes.
|
|
||||||
msg := errors.New("/* nil */")
|
|
||||||
fv = reflect.ValueOf(&msg).Elem()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writeName(w, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b, ok := fv.Interface().(raw); ok {
|
|
||||||
if err := writeRaw(w, b.Bytes()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums have a String method, so writeAny will work fine.
|
|
||||||
if err := tm.writeAny(w, fv, props); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extensions (the XXX_extensions field).
|
|
||||||
pv := sv.Addr()
|
|
||||||
if _, ok := extendable(pv.Interface()); ok {
|
|
||||||
if err := tm.writeExtensions(w, pv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeRaw writes an uninterpreted raw message.
|
|
||||||
func writeRaw(w *textWriter, b []byte) error {
|
|
||||||
if err := w.WriteByte('<'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.indent()
|
|
||||||
if err := writeUnknownStruct(w, b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.unindent()
|
|
||||||
if err := w.WriteByte('>'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeAny writes an arbitrary field.
|
|
||||||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
||||||
v = reflect.Indirect(v)
|
|
||||||
|
|
||||||
// Floats have special cases.
|
|
||||||
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
|
|
||||||
x := v.Float()
|
|
||||||
var b []byte
|
|
||||||
switch {
|
|
||||||
case math.IsInf(x, 1):
|
|
||||||
b = posInf
|
|
||||||
case math.IsInf(x, -1):
|
|
||||||
b = negInf
|
|
||||||
case math.IsNaN(x):
|
|
||||||
b = nan
|
|
||||||
}
|
|
||||||
if b != nil {
|
|
||||||
_, err := w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Other values are handled below.
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't attempt to serialise every possible value type; only those
|
|
||||||
// that can occur in protocol buffers.
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
// Should only be a []byte; repeated fields are handled in writeStruct.
|
|
||||||
if err := writeString(w, string(v.Bytes())); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
if err := writeString(w, v.String()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
// Required/optional group/message.
|
|
||||||
var bra, ket byte = '<', '>'
|
|
||||||
if props != nil && props.Wire == "group" {
|
|
||||||
bra, ket = '{', '}'
|
|
||||||
}
|
|
||||||
if err := w.WriteByte(bra); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.indent()
|
|
||||||
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
||||||
text, err := etm.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = w.Write(text); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if err := tm.writeStruct(w, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.unindent()
|
|
||||||
if err := w.WriteByte(ket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
_, err := fmt.Fprint(w, v.Interface())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// equivalent to C's isprint.
|
|
||||||
func isprint(c byte) bool {
|
|
||||||
return c >= 0x20 && c < 0x7f
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeString writes a string in the protocol buffer text format.
|
|
||||||
// It is similar to strconv.Quote except we don't use Go escape sequences,
|
|
||||||
// we treat the string as a byte sequence, and we use octal escapes.
|
|
||||||
// These differences are to maintain interoperability with the other
|
|
||||||
// languages' implementations of the text format.
|
|
||||||
func writeString(w *textWriter, s string) error {
|
|
||||||
// use WriteByte here to get any needed indent
|
|
||||||
if err := w.WriteByte('"'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Loop over the bytes, not the runes.
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
var err error
|
|
||||||
// Divergence from C++: we don't escape apostrophes.
|
|
||||||
// There's no need to escape them, and the C++ parser
|
|
||||||
// copes with a naked apostrophe.
|
|
||||||
switch c := s[i]; c {
|
|
||||||
case '\n':
|
|
||||||
_, err = w.w.Write(backslashN)
|
|
||||||
case '\r':
|
|
||||||
_, err = w.w.Write(backslashR)
|
|
||||||
case '\t':
|
|
||||||
_, err = w.w.Write(backslashT)
|
|
||||||
case '"':
|
|
||||||
_, err = w.w.Write(backslashDQ)
|
|
||||||
case '\\':
|
|
||||||
_, err = w.w.Write(backslashBS)
|
|
||||||
default:
|
|
||||||
if isprint(c) {
|
|
||||||
err = w.w.WriteByte(c)
|
|
||||||
} else {
|
|
||||||
_, err = fmt.Fprintf(w.w, "\\%03o", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return w.WriteByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
|
|
||||||
if !w.compact {
|
|
||||||
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b := NewBuffer(data)
|
|
||||||
for b.index < len(b.buf) {
|
|
||||||
x, err := b.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
_, err := fmt.Fprintf(w, "/* %v */\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
wire, tag := x&7, x>>3
|
|
||||||
if wire == WireEndGroup {
|
|
||||||
w.unindent()
|
|
||||||
if _, err := w.Write(endBraceNewline); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprint(w, tag); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if wire != WireStartGroup {
|
|
||||||
if err := w.WriteByte(':'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !w.compact || wire == WireStartGroup {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch wire {
|
|
||||||
case WireBytes:
|
|
||||||
buf, e := b.DecodeRawBytes(false)
|
|
||||||
if e == nil {
|
|
||||||
_, err = fmt.Fprintf(w, "%q", buf)
|
|
||||||
} else {
|
|
||||||
_, err = fmt.Fprintf(w, "/* %v */", e)
|
|
||||||
}
|
|
||||||
case WireFixed32:
|
|
||||||
x, err = b.DecodeFixed32()
|
|
||||||
err = writeUnknownInt(w, x, err)
|
|
||||||
case WireFixed64:
|
|
||||||
x, err = b.DecodeFixed64()
|
|
||||||
err = writeUnknownInt(w, x, err)
|
|
||||||
case WireStartGroup:
|
|
||||||
err = w.WriteByte('{')
|
|
||||||
w.indent()
|
|
||||||
case WireVarint:
|
|
||||||
x, err = b.DecodeVarint()
|
|
||||||
err = writeUnknownInt(w, x, err)
|
|
||||||
default:
|
|
||||||
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeUnknownInt(w *textWriter, x uint64, err error) error {
|
|
||||||
if err == nil {
|
|
||||||
_, err = fmt.Fprint(w, x)
|
|
||||||
} else {
|
|
||||||
_, err = fmt.Fprintf(w, "/* %v */", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type int32Slice []int32
|
|
||||||
|
|
||||||
func (s int32Slice) Len() int { return len(s) }
|
|
||||||
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
|
|
||||||
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
// writeExtensions writes all the extensions in pv.
|
|
||||||
// pv is assumed to be a pointer to a protocol message struct that is extendable.
|
|
||||||
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
||||||
emap := extensionMaps[pv.Type().Elem()]
|
|
||||||
ep, _ := extendable(pv.Interface())
|
|
||||||
|
|
||||||
// Order the extensions by ID.
|
|
||||||
// This isn't strictly necessary, but it will give us
|
|
||||||
// canonical output, which will also make testing easier.
|
|
||||||
m, mu := ep.extensionsRead()
|
|
||||||
if m == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
ids := make([]int32, 0, len(m))
|
|
||||||
for id := range m {
|
|
||||||
ids = append(ids, id)
|
|
||||||
}
|
|
||||||
sort.Sort(int32Slice(ids))
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
for _, extNum := range ids {
|
|
||||||
ext := m[extNum]
|
|
||||||
var desc *ExtensionDesc
|
|
||||||
if emap != nil {
|
|
||||||
desc = emap[extNum]
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
// Unknown extension.
|
|
||||||
if err := writeUnknownStruct(w, ext.enc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pb, err := GetExtension(ep, desc)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed getting extension: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repeated extensions will appear as a slice.
|
|
||||||
if !desc.repeated() {
|
|
||||||
if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v := reflect.ValueOf(pb)
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
||||||
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !w.compact {
|
|
||||||
if err := w.WriteByte(' '); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteByte('\n'); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeIndent() {
|
|
||||||
if !w.complete {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
remain := w.ind * 2
|
|
||||||
for remain > 0 {
|
|
||||||
n := remain
|
|
||||||
if n > len(spaces) {
|
|
||||||
n = len(spaces)
|
|
||||||
}
|
|
||||||
w.w.Write(spaces[:n])
|
|
||||||
remain -= n
|
|
||||||
}
|
|
||||||
w.complete = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextMarshaler is a configurable text format marshaler.
|
|
||||||
type TextMarshaler struct {
|
|
||||||
Compact bool // use compact text format (one line).
|
|
||||||
ExpandAny bool // expand google.protobuf.Any messages of known types
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal writes a given protocol buffer in text format.
|
|
||||||
// The only errors returned are from w.
|
|
||||||
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
|
||||||
val := reflect.ValueOf(pb)
|
|
||||||
if pb == nil || val.IsNil() {
|
|
||||||
w.Write([]byte("<nil>"))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var bw *bufio.Writer
|
|
||||||
ww, ok := w.(writer)
|
|
||||||
if !ok {
|
|
||||||
bw = bufio.NewWriter(w)
|
|
||||||
ww = bw
|
|
||||||
}
|
|
||||||
aw := &textWriter{
|
|
||||||
w: ww,
|
|
||||||
complete: true,
|
|
||||||
compact: tm.Compact,
|
|
||||||
}
|
|
||||||
|
|
||||||
if etm, ok := pb.(encoding.TextMarshaler); ok {
|
|
||||||
text, err := etm.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = aw.Write(text); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bw != nil {
|
|
||||||
return bw.Flush()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Dereference the received pointer so we don't have outer < and >.
|
|
||||||
v := reflect.Indirect(val)
|
|
||||||
if err := tm.writeStruct(aw, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bw != nil {
|
|
||||||
return bw.Flush()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text is the same as Marshal, but returns the string directly.
|
|
||||||
func (tm *TextMarshaler) Text(pb Message) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
tm.Marshal(&buf, pb)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultTextMarshaler = TextMarshaler{}
|
|
||||||
compactTextMarshaler = TextMarshaler{Compact: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: consider removing some of the Marshal functions below.
|
|
||||||
|
|
||||||
// MarshalText writes a given protocol buffer in text format.
|
|
||||||
// The only errors returned are from w.
|
|
||||||
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
|
||||||
|
|
||||||
// MarshalTextString is the same as MarshalText, but returns the string directly.
|
|
||||||
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
|
||||||
|
|
||||||
// CompactText writes a given protocol buffer in compact text format (one line).
|
|
||||||
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
|
||||||
|
|
||||||
// CompactTextString is the same as CompactText, but returns the string directly.
|
|
||||||
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|
|
|
@ -1,895 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
// Functions for parsing the Text protocol buffer format.
|
|
||||||
// TODO: message sets.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error string emitted when deserializing Any and fields are already set
|
|
||||||
const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
|
|
||||||
|
|
||||||
type ParseError struct {
|
|
||||||
Message string
|
|
||||||
Line int // 1-based line number
|
|
||||||
Offset int // 0-based byte offset from start of input
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ParseError) Error() string {
|
|
||||||
if p.Line == 1 {
|
|
||||||
// show offset only for first line
|
|
||||||
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("line %d: %v", p.Line, p.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
type token struct {
|
|
||||||
value string
|
|
||||||
err *ParseError
|
|
||||||
line int // line number
|
|
||||||
offset int // byte number from start of input, not start of line
|
|
||||||
unquoted string // the unquoted version of value, if it was a quoted string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *token) String() string {
|
|
||||||
if t.err == nil {
|
|
||||||
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("parse error: %v", t.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type textParser struct {
|
|
||||||
s string // remaining input
|
|
||||||
done bool // whether the parsing is finished (success or error)
|
|
||||||
backed bool // whether back() was called
|
|
||||||
offset, line int
|
|
||||||
cur token
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTextParser(s string) *textParser {
|
|
||||||
p := new(textParser)
|
|
||||||
p.s = s
|
|
||||||
p.line = 1
|
|
||||||
p.cur.line = 1
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
|
||||||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
|
||||||
p.cur.err = pe
|
|
||||||
p.done = true
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
|
|
||||||
// Numbers and identifiers are matched by [-+._A-Za-z0-9]
|
|
||||||
func isIdentOrNumberChar(c byte) bool {
|
|
||||||
switch {
|
|
||||||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
|
||||||
return true
|
|
||||||
case '0' <= c && c <= '9':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch c {
|
|
||||||
case '-', '+', '.', '_':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isWhitespace(c byte) bool {
|
|
||||||
switch c {
|
|
||||||
case ' ', '\t', '\n', '\r':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isQuote(c byte) bool {
|
|
||||||
switch c {
|
|
||||||
case '"', '\'':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) skipWhitespace() {
|
|
||||||
i := 0
|
|
||||||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
|
||||||
if p.s[i] == '#' {
|
|
||||||
// comment; skip to end of line or input
|
|
||||||
for i < len(p.s) && p.s[i] != '\n' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == len(p.s) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p.s[i] == '\n' {
|
|
||||||
p.line++
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
p.offset += i
|
|
||||||
p.s = p.s[i:len(p.s)]
|
|
||||||
if len(p.s) == 0 {
|
|
||||||
p.done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) advance() {
|
|
||||||
// Skip whitespace
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.done {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start of non-whitespace
|
|
||||||
p.cur.err = nil
|
|
||||||
p.cur.offset, p.cur.line = p.offset, p.line
|
|
||||||
p.cur.unquoted = ""
|
|
||||||
switch p.s[0] {
|
|
||||||
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
|
||||||
// Single symbol
|
|
||||||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
|
||||||
case '"', '\'':
|
|
||||||
// Quoted string
|
|
||||||
i := 1
|
|
||||||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
|
||||||
if p.s[i] == '\\' && i+1 < len(p.s) {
|
|
||||||
// skip escaped char
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i >= len(p.s) || p.s[i] != p.s[0] {
|
|
||||||
p.errorf("unmatched quote")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
|
||||||
p.cur.unquoted = unq
|
|
||||||
default:
|
|
||||||
i := 0
|
|
||||||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
p.errorf("unexpected byte %#x", p.s[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
|
||||||
}
|
|
||||||
p.offset += len(p.cur.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errBadUTF8 = errors.New("proto: bad UTF-8")
|
|
||||||
errBadHex = errors.New("proto: bad hexadecimal")
|
|
||||||
)
|
|
||||||
|
|
||||||
func unquoteC(s string, quote rune) (string, error) {
|
|
||||||
// This is based on C++'s tokenizer.cc.
|
|
||||||
// Despite its name, this is *not* parsing C syntax.
|
|
||||||
// For instance, "\0" is an invalid quoted string.
|
|
||||||
|
|
||||||
// Avoid allocation in trivial cases.
|
|
||||||
simple := true
|
|
||||||
for _, r := range s {
|
|
||||||
if r == '\\' || r == quote {
|
|
||||||
simple = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if simple {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 0, 3*len(s)/2)
|
|
||||||
for len(s) > 0 {
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
return "", errBadUTF8
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
if r != '\\' {
|
|
||||||
if r < utf8.RuneSelf {
|
|
||||||
buf = append(buf, byte(r))
|
|
||||||
} else {
|
|
||||||
buf = append(buf, string(r)...)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, tail, err := unescape(s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf = append(buf, ch...)
|
|
||||||
s = tail
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unescape(s string) (ch string, tail string, err error) {
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
return "", "", errBadUTF8
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
switch r {
|
|
||||||
case 'a':
|
|
||||||
return "\a", s, nil
|
|
||||||
case 'b':
|
|
||||||
return "\b", s, nil
|
|
||||||
case 'f':
|
|
||||||
return "\f", s, nil
|
|
||||||
case 'n':
|
|
||||||
return "\n", s, nil
|
|
||||||
case 'r':
|
|
||||||
return "\r", s, nil
|
|
||||||
case 't':
|
|
||||||
return "\t", s, nil
|
|
||||||
case 'v':
|
|
||||||
return "\v", s, nil
|
|
||||||
case '?':
|
|
||||||
return "?", s, nil // trigraph workaround
|
|
||||||
case '\'', '"', '\\':
|
|
||||||
return string(r), s, nil
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X':
|
|
||||||
if len(s) < 2 {
|
|
||||||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
|
||||||
}
|
|
||||||
base := 8
|
|
||||||
ss := s[:2]
|
|
||||||
s = s[2:]
|
|
||||||
if r == 'x' || r == 'X' {
|
|
||||||
base = 16
|
|
||||||
} else {
|
|
||||||
ss = string(r) + ss
|
|
||||||
}
|
|
||||||
i, err := strconv.ParseUint(ss, base, 8)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
return string([]byte{byte(i)}), s, nil
|
|
||||||
case 'u', 'U':
|
|
||||||
n := 4
|
|
||||||
if r == 'U' {
|
|
||||||
n = 8
|
|
||||||
}
|
|
||||||
if len(s) < n {
|
|
||||||
return "", "", fmt.Errorf(`\%c requires %d digits`, r, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
bs := make([]byte, n/2)
|
|
||||||
for i := 0; i < n; i += 2 {
|
|
||||||
a, ok1 := unhex(s[i])
|
|
||||||
b, ok2 := unhex(s[i+1])
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
return "", "", errBadHex
|
|
||||||
}
|
|
||||||
bs[i/2] = a<<4 | b
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
return string(bs), s, nil
|
|
||||||
}
|
|
||||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapted from src/pkg/strconv/quote.go.
|
|
||||||
func unhex(b byte) (v byte, ok bool) {
|
|
||||||
switch {
|
|
||||||
case '0' <= b && b <= '9':
|
|
||||||
return b - '0', true
|
|
||||||
case 'a' <= b && b <= 'f':
|
|
||||||
return b - 'a' + 10, true
|
|
||||||
case 'A' <= b && b <= 'F':
|
|
||||||
return b - 'A' + 10, true
|
|
||||||
}
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back off the parser by one token. Can only be done between calls to next().
|
|
||||||
// It makes the next advance() a no-op.
|
|
||||||
func (p *textParser) back() { p.backed = true }
|
|
||||||
|
|
||||||
// Advances the parser and returns the new current token.
|
|
||||||
func (p *textParser) next() *token {
|
|
||||||
if p.backed || p.done {
|
|
||||||
p.backed = false
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
p.advance()
|
|
||||||
if p.done {
|
|
||||||
p.cur.value = ""
|
|
||||||
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
|
||||||
// Look for multiple quoted strings separated by whitespace,
|
|
||||||
// and concatenate them.
|
|
||||||
cat := p.cur
|
|
||||||
for {
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.done || !isQuote(p.s[0]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.advance()
|
|
||||||
if p.cur.err != nil {
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
cat.value += " " + p.cur.value
|
|
||||||
cat.unquoted += p.cur.unquoted
|
|
||||||
}
|
|
||||||
p.done = false // parser may have seen EOF, but we want to return cat
|
|
||||||
p.cur = cat
|
|
||||||
}
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) consumeToken(s string) error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != s {
|
|
||||||
p.back()
|
|
||||||
return p.errorf("expected %q, found %q", s, tok.value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a RequiredNotSetError indicating which required field was not set.
|
|
||||||
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
|
|
||||||
st := sv.Type()
|
|
||||||
sprops := GetProperties(st)
|
|
||||||
for i := 0; i < st.NumField(); i++ {
|
|
||||||
if !isNil(sv.Field(i)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
props := sprops.Prop[i]
|
|
||||||
if props.Required {
|
|
||||||
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index in the struct for the named field, as well as the parsed tag properties.
|
|
||||||
func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
|
|
||||||
i, ok := sprops.decoderOrigNames[name]
|
|
||||||
if ok {
|
|
||||||
return i, sprops.Prop[i], true
|
|
||||||
}
|
|
||||||
return -1, nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume a ':' from the input stream (if the next token is a colon),
|
|
||||||
// returning an error if a colon is needed but not present.
|
|
||||||
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != ":" {
|
|
||||||
// Colon is optional when the field is a group or message.
|
|
||||||
needColon := true
|
|
||||||
switch props.Wire {
|
|
||||||
case "group":
|
|
||||||
needColon = false
|
|
||||||
case "bytes":
|
|
||||||
// A "bytes" field is either a message, a string, or a repeated field;
|
|
||||||
// those three become *T, *string and []T respectively, so we can check for
|
|
||||||
// this field being a pointer to a non-string.
|
|
||||||
if typ.Kind() == reflect.Ptr {
|
|
||||||
// *T or *string
|
|
||||||
if typ.Elem().Kind() == reflect.String {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if typ.Kind() == reflect.Slice {
|
|
||||||
// []T or []*T
|
|
||||||
if typ.Elem().Kind() != reflect.Ptr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if typ.Kind() == reflect.String {
|
|
||||||
// The proto3 exception is for a string field,
|
|
||||||
// which requires a colon.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
needColon = false
|
|
||||||
}
|
|
||||||
if needColon {
|
|
||||||
return p.errorf("expected ':', found %q", tok.value)
|
|
||||||
}
|
|
||||||
p.back()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
|
||||||
st := sv.Type()
|
|
||||||
sprops := GetProperties(st)
|
|
||||||
reqCount := sprops.reqCount
|
|
||||||
var reqFieldErr error
|
|
||||||
fieldSet := make(map[string]bool)
|
|
||||||
// A struct is a sequence of "name: value", terminated by one of
|
|
||||||
// '>' or '}', or the end of the input. A name may also be
|
|
||||||
// "[extension]" or "[type/url]".
|
|
||||||
//
|
|
||||||
// The whole struct can also be an expanded Any message, like:
|
|
||||||
// [type/url] < ... struct contents ... >
|
|
||||||
for {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == terminator {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if tok.value == "[" {
|
|
||||||
// Looks like an extension or an Any.
|
|
||||||
//
|
|
||||||
// TODO: Check whether we need to handle
|
|
||||||
// namespace rooted names (e.g. ".something.Foo").
|
|
||||||
extName, err := p.consumeExtName()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s := strings.LastIndex(extName, "/"); s >= 0 {
|
|
||||||
// If it contains a slash, it's an Any type URL.
|
|
||||||
messageName := extName[s+1:]
|
|
||||||
mt := MessageType(messageName)
|
|
||||||
if mt == nil {
|
|
||||||
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
|
|
||||||
}
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
// consume an optional colon
|
|
||||||
if tok.value == ":" {
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
v := reflect.New(mt.Elem())
|
|
||||||
if pe := p.readStruct(v.Elem(), terminator); pe != nil {
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
b, err := Marshal(v.Interface().(Message))
|
|
||||||
if err != nil {
|
|
||||||
return p.errorf("failed to marshal message of type %q: %v", messageName, err)
|
|
||||||
}
|
|
||||||
if fieldSet["type_url"] {
|
|
||||||
return p.errorf(anyRepeatedlyUnpacked, "type_url")
|
|
||||||
}
|
|
||||||
if fieldSet["value"] {
|
|
||||||
return p.errorf(anyRepeatedlyUnpacked, "value")
|
|
||||||
}
|
|
||||||
sv.FieldByName("TypeUrl").SetString(extName)
|
|
||||||
sv.FieldByName("Value").SetBytes(b)
|
|
||||||
fieldSet["type_url"] = true
|
|
||||||
fieldSet["value"] = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var desc *ExtensionDesc
|
|
||||||
// This could be faster, but it's functional.
|
|
||||||
// TODO: Do something smarter than a linear scan.
|
|
||||||
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
|
||||||
if d.Name == extName {
|
|
||||||
desc = d
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
return p.errorf("unrecognized extension %q", extName)
|
|
||||||
}
|
|
||||||
|
|
||||||
props := &Properties{}
|
|
||||||
props.Parse(desc.Tag)
|
|
||||||
|
|
||||||
typ := reflect.TypeOf(desc.ExtensionType)
|
|
||||||
if err := p.checkForColon(props, typ); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rep := desc.repeated()
|
|
||||||
|
|
||||||
// Read the extension structure, and set it in
|
|
||||||
// the value we're constructing.
|
|
||||||
var ext reflect.Value
|
|
||||||
if !rep {
|
|
||||||
ext = reflect.New(typ).Elem()
|
|
||||||
} else {
|
|
||||||
ext = reflect.New(typ.Elem()).Elem()
|
|
||||||
}
|
|
||||||
if err := p.readAny(ext, props); err != nil {
|
|
||||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reqFieldErr = err
|
|
||||||
}
|
|
||||||
ep := sv.Addr().Interface().(Message)
|
|
||||||
if !rep {
|
|
||||||
SetExtension(ep, desc, ext.Interface())
|
|
||||||
} else {
|
|
||||||
old, err := GetExtension(ep, desc)
|
|
||||||
var sl reflect.Value
|
|
||||||
if err == nil {
|
|
||||||
sl = reflect.ValueOf(old) // existing slice
|
|
||||||
} else {
|
|
||||||
sl = reflect.MakeSlice(typ, 0, 1)
|
|
||||||
}
|
|
||||||
sl = reflect.Append(sl, ext)
|
|
||||||
SetExtension(ep, desc, sl.Interface())
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a normal, non-extension field.
|
|
||||||
name := tok.value
|
|
||||||
var dst reflect.Value
|
|
||||||
fi, props, ok := structFieldByName(sprops, name)
|
|
||||||
if ok {
|
|
||||||
dst = sv.Field(fi)
|
|
||||||
} else if oop, ok := sprops.OneofTypes[name]; ok {
|
|
||||||
// It is a oneof.
|
|
||||||
props = oop.Prop
|
|
||||||
nv := reflect.New(oop.Type.Elem())
|
|
||||||
dst = nv.Elem().Field(0)
|
|
||||||
field := sv.Field(oop.Field)
|
|
||||||
if !field.IsNil() {
|
|
||||||
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
|
|
||||||
}
|
|
||||||
field.Set(nv)
|
|
||||||
}
|
|
||||||
if !dst.IsValid() {
|
|
||||||
return p.errorf("unknown field name %q in %v", name, st)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dst.Kind() == reflect.Map {
|
|
||||||
// Consume any colon.
|
|
||||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the map if it doesn't already exist.
|
|
||||||
if dst.IsNil() {
|
|
||||||
dst.Set(reflect.MakeMap(dst.Type()))
|
|
||||||
}
|
|
||||||
key := reflect.New(dst.Type().Key()).Elem()
|
|
||||||
val := reflect.New(dst.Type().Elem()).Elem()
|
|
||||||
|
|
||||||
// The map entry should be this sequence of tokens:
|
|
||||||
// < key : KEY value : VALUE >
|
|
||||||
// However, implementations may omit key or value, and technically
|
|
||||||
// we should support them in any order. See b/28924776 for a time
|
|
||||||
// this went wrong.
|
|
||||||
|
|
||||||
tok := p.next()
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == terminator {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch tok.value {
|
|
||||||
case "key":
|
|
||||||
if err := p.consumeToken(":"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.readAny(key, props.mkeyprop); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "value":
|
|
||||||
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.readAny(val, props.mvalprop); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
p.back()
|
|
||||||
return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.SetMapIndex(key, val)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that it's not already set if it's not a repeated field.
|
|
||||||
if !props.Repeated && fieldSet[name] {
|
|
||||||
return p.errorf("non-repeated field %q was repeated", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.checkForColon(props, dst.Type()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse into the field.
|
|
||||||
fieldSet[name] = true
|
|
||||||
if err := p.readAny(dst, props); err != nil {
|
|
||||||
if _, ok := err.(*RequiredNotSetError); !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reqFieldErr = err
|
|
||||||
}
|
|
||||||
if props.Required {
|
|
||||||
reqCount--
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if reqCount > 0 {
|
|
||||||
return p.missingRequiredFieldError(sv)
|
|
||||||
}
|
|
||||||
return reqFieldErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeExtName consumes extension name or expanded Any type URL and the
|
|
||||||
// following ']'. It returns the name or URL consumed.
|
|
||||||
func (p *textParser) consumeExtName() (string, error) {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return "", tok.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If extension name or type url is quoted, it's a single token.
|
|
||||||
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
|
||||||
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return name, p.consumeToken("]")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume everything up to "]"
|
|
||||||
var parts []string
|
|
||||||
for tok.value != "]" {
|
|
||||||
parts = append(parts, tok.value)
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(parts, ""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
|
||||||
// It is used in readStruct to provide backward compatibility.
|
|
||||||
func (p *textParser) consumeOptionalSeparator() error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != ";" && tok.value != "," {
|
|
||||||
p.back()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) readAny(v reflect.Value, props *Properties) error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "" {
|
|
||||||
return p.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch fv := v; fv.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
at := v.Type()
|
|
||||||
if at.Elem().Kind() == reflect.Uint8 {
|
|
||||||
// Special case for []byte
|
|
||||||
if tok.value[0] != '"' && tok.value[0] != '\'' {
|
|
||||||
// Deliberately written out here, as the error after
|
|
||||||
// this switch statement would write "invalid []byte: ...",
|
|
||||||
// which is not as user-friendly.
|
|
||||||
return p.errorf("invalid string: %v", tok.value)
|
|
||||||
}
|
|
||||||
bytes := []byte(tok.unquoted)
|
|
||||||
fv.Set(reflect.ValueOf(bytes))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Repeated field.
|
|
||||||
if tok.value == "[" {
|
|
||||||
// Repeated field with list notation, like [1,2,3].
|
|
||||||
for {
|
|
||||||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
|
||||||
err := p.readAny(fv.Index(fv.Len()-1), props)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "]" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if tok.value != "," {
|
|
||||||
return p.errorf("Expected ']' or ',' found %q", tok.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// One value of the repeated field.
|
|
||||||
p.back()
|
|
||||||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
|
|
||||||
return p.readAny(fv.Index(fv.Len()-1), props)
|
|
||||||
case reflect.Bool:
|
|
||||||
// true/1/t/True or false/f/0/False.
|
|
||||||
switch tok.value {
|
|
||||||
case "true", "1", "t", "True":
|
|
||||||
fv.SetBool(true)
|
|
||||||
return nil
|
|
||||||
case "false", "0", "f", "False":
|
|
||||||
fv.SetBool(false)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
v := tok.value
|
|
||||||
// Ignore 'f' for compatibility with output generated by C++, but don't
|
|
||||||
// remove 'f' when the value is "-inf" or "inf".
|
|
||||||
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
|
|
||||||
v = v[:len(v)-1]
|
|
||||||
}
|
|
||||||
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
|
|
||||||
fv.SetFloat(f)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Int32:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
|
||||||
fv.SetInt(x)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(props.Enum) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
m, ok := enumValueMaps[props.Enum]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
x, ok := m[tok.value]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fv.SetInt(int64(x))
|
|
||||||
return nil
|
|
||||||
case reflect.Int64:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
|
||||||
fv.SetInt(x)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Ptr:
|
|
||||||
// A basic field (indirected through pointer), or a repeated message/group
|
|
||||||
p.back()
|
|
||||||
fv.Set(reflect.New(fv.Type().Elem()))
|
|
||||||
return p.readAny(fv.Elem(), props)
|
|
||||||
case reflect.String:
|
|
||||||
if tok.value[0] == '"' || tok.value[0] == '\'' {
|
|
||||||
fv.SetString(tok.unquoted)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
|
|
||||||
return p.readStruct(fv, terminator)
|
|
||||||
case reflect.Uint32:
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
|
||||||
fv.SetUint(uint64(x))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Uint64:
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
|
||||||
fv.SetUint(x)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.errorf("invalid %v: %v", v.Type(), tok.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
|
|
||||||
// before starting to unmarshal, so any existing data in pb is always removed.
|
|
||||||
// If a required field is not set and no other error occurs,
|
|
||||||
// UnmarshalText returns *RequiredNotSetError.
|
|
||||||
func UnmarshalText(s string, pb Message) error {
|
|
||||||
if um, ok := pb.(encoding.TextUnmarshaler); ok {
|
|
||||||
err := um.UnmarshalText([]byte(s))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pb.Reset()
|
|
||||||
v := reflect.ValueOf(pb)
|
|
||||||
if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,673 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/golang/protobuf/proto"
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
. "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UnmarshalTextTest struct {
|
|
||||||
in string
|
|
||||||
err string // if "", no error expected
|
|
||||||
out *MyMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildExtStructTest(text string) UnmarshalTextTest {
|
|
||||||
msg := &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
}
|
|
||||||
SetExtension(msg, E_Ext_More, &Ext{
|
|
||||||
Data: String("Hello, world!"),
|
|
||||||
})
|
|
||||||
return UnmarshalTextTest{in: text, out: msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildExtDataTest(text string) UnmarshalTextTest {
|
|
||||||
msg := &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
}
|
|
||||||
SetExtension(msg, E_Ext_Text, String("Hello, world!"))
|
|
||||||
SetExtension(msg, E_Ext_Number, Int32(1729))
|
|
||||||
return UnmarshalTextTest{in: text, out: msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildExtRepStringTest(text string) UnmarshalTextTest {
|
|
||||||
msg := &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
}
|
|
||||||
if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return UnmarshalTextTest{in: text, out: msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
var unMarshalTextTests = []UnmarshalTextTest{
|
|
||||||
// Basic
|
|
||||||
{
|
|
||||||
in: " count:42\n name:\"Dave\" ",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("Dave"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Empty quoted string
|
|
||||||
{
|
|
||||||
in: `count:42 name:""`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String(""),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string concatenation with double quotes
|
|
||||||
{
|
|
||||||
in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string concatenation with single quotes
|
|
||||||
{
|
|
||||||
in: "count:42 name: 'My name is '\n'elsewhere'",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string concatenations with mixed quotes
|
|
||||||
{
|
|
||||||
in: "count:42 name: 'My name is '\n\"elsewhere\"",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
in: "count:42 name: \"My name is \"\n'elsewhere'",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with escaped apostrophe
|
|
||||||
{
|
|
||||||
in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("HOLIDAY - New Year's Day"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with single quote
|
|
||||||
{
|
|
||||||
in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String(`Roger "The Ramster" Ramjet`),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with all the accepted special characters from the C++ test
|
|
||||||
{
|
|
||||||
in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with quoted backslash
|
|
||||||
{
|
|
||||||
in: `count:42 name: "\\'xyz"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String(`\'xyz`),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with UTF-8 bytes.
|
|
||||||
{
|
|
||||||
in: "count:42 name: '\303\277\302\201\xAB'",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("\303\277\302\201\xAB"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Bad quoted string
|
|
||||||
{
|
|
||||||
in: `inner: < host: "\0" >` + "\n",
|
|
||||||
err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number too large for int64
|
|
||||||
{
|
|
||||||
in: "count: 1 others { key: 123456789012345678901 }",
|
|
||||||
err: "line 1.23: invalid int64: 123456789012345678901",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number too large for int32
|
|
||||||
{
|
|
||||||
in: "count: 1234567890123",
|
|
||||||
err: "line 1.7: invalid int32: 1234567890123",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number in hexadecimal
|
|
||||||
{
|
|
||||||
in: "count: 0x2beef",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(0x2beef),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number in octal
|
|
||||||
{
|
|
||||||
in: "count: 024601",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(024601),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Floating point number with "f" suffix
|
|
||||||
{
|
|
||||||
in: "count: 4 others:< weight: 17.0f >",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Others: []*OtherMessage{
|
|
||||||
{
|
|
||||||
Weight: Float32(17),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Floating point positive infinity
|
|
||||||
{
|
|
||||||
in: "count: 4 bigfloat: inf",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Bigfloat: Float64(math.Inf(1)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Floating point negative infinity
|
|
||||||
{
|
|
||||||
in: "count: 4 bigfloat: -inf",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Bigfloat: Float64(math.Inf(-1)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number too large for float32
|
|
||||||
{
|
|
||||||
in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
|
|
||||||
err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number posing as a quoted string
|
|
||||||
{
|
|
||||||
in: `inner: < host: 12 >` + "\n",
|
|
||||||
err: `line 1.15: invalid string: 12`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string posing as int32
|
|
||||||
{
|
|
||||||
in: `count: "12"`,
|
|
||||||
err: `line 1.7: invalid int32: "12"`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string posing a float32
|
|
||||||
{
|
|
||||||
in: `others:< weight: "17.4" >`,
|
|
||||||
err: `line 1.17: invalid float32: "17.4"`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enum
|
|
||||||
{
|
|
||||||
in: `count:42 bikeshed: BLUE`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Bikeshed: MyMessage_BLUE.Enum(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated field
|
|
||||||
{
|
|
||||||
in: `count:42 pet: "horsey" pet:"bunny"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Pet: []string{"horsey", "bunny"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated field with list notation
|
|
||||||
{
|
|
||||||
in: `count:42 pet: ["horsey", "bunny"]`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Pet: []string{"horsey", "bunny"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated message with/without colon and <>/{}
|
|
||||||
{
|
|
||||||
in: `count:42 others:{} others{} others:<> others:{}`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Others: []*OtherMessage{
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing colon for inner message
|
|
||||||
{
|
|
||||||
in: `count:42 inner < host: "cauchy.syd" >`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("cauchy.syd"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing colon for string field
|
|
||||||
{
|
|
||||||
in: `name "Dave"`,
|
|
||||||
err: `line 1.5: expected ':', found "\"Dave\""`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing colon for int32 field
|
|
||||||
{
|
|
||||||
in: `count 42`,
|
|
||||||
err: `line 1.6: expected ':', found "42"`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing required field
|
|
||||||
{
|
|
||||||
in: `name: "Pawel"`,
|
|
||||||
err: `proto: required field "testdata.MyMessage.count" not set`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Name: String("Pawel"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing required field in a required submessage
|
|
||||||
{
|
|
||||||
in: `count: 42 we_must_go_deeper < leo_finally_won_an_oscar <> >`,
|
|
||||||
err: `proto: required field "testdata.InnerMessage.host" not set`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
WeMustGoDeeper: &RequiredInnerMessage{LeoFinallyWonAnOscar: &InnerMessage{}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated non-repeated field
|
|
||||||
{
|
|
||||||
in: `name: "Rob" name: "Russ"`,
|
|
||||||
err: `line 1.12: non-repeated field "name" was repeated`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Group
|
|
||||||
{
|
|
||||||
in: `count: 17 SomeGroup { group_field: 12 }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(17),
|
|
||||||
Somegroup: &MyMessage_SomeGroup{
|
|
||||||
GroupField: Int32(12),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Semicolon between fields
|
|
||||||
{
|
|
||||||
in: `count:3;name:"Calvin"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(3),
|
|
||||||
Name: String("Calvin"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Comma between fields
|
|
||||||
{
|
|
||||||
in: `count:4,name:"Ezekiel"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Name: String("Ezekiel"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Boolean false
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: false }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(false),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean true
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: true }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean 0
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: 0 }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(false),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean 1
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: 1 }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean f
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: f }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(false),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean t
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: t }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean False
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: False }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(false),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Boolean True
|
|
||||||
{
|
|
||||||
in: `count:42 inner { host: "example.com" connected: True }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("example.com"),
|
|
||||||
Connected: Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Extension
|
|
||||||
buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
|
|
||||||
buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
|
|
||||||
buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
|
|
||||||
buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
|
|
||||||
|
|
||||||
// Big all-in-one
|
|
||||||
{
|
|
||||||
in: "count:42 # Meaning\n" +
|
|
||||||
`name:"Dave" ` +
|
|
||||||
`quote:"\"I didn't want to go.\"" ` +
|
|
||||||
`pet:"bunny" ` +
|
|
||||||
`pet:"kitty" ` +
|
|
||||||
`pet:"horsey" ` +
|
|
||||||
`inner:<` +
|
|
||||||
` host:"footrest.syd" ` +
|
|
||||||
` port:7001 ` +
|
|
||||||
` connected:true ` +
|
|
||||||
`> ` +
|
|
||||||
`others:<` +
|
|
||||||
` key:3735928559 ` +
|
|
||||||
` value:"\x01A\a\f" ` +
|
|
||||||
`> ` +
|
|
||||||
`others:<` +
|
|
||||||
" weight:58.9 # Atomic weight of Co\n" +
|
|
||||||
` inner:<` +
|
|
||||||
` host:"lesha.mtv" ` +
|
|
||||||
` port:8002 ` +
|
|
||||||
` >` +
|
|
||||||
`>`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("Dave"),
|
|
||||||
Quote: String(`"I didn't want to go."`),
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("footrest.syd"),
|
|
||||||
Port: Int32(7001),
|
|
||||||
Connected: Bool(true),
|
|
||||||
},
|
|
||||||
Others: []*OtherMessage{
|
|
||||||
{
|
|
||||||
Key: Int64(3735928559),
|
|
||||||
Value: []byte{0x1, 'A', '\a', '\f'},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Weight: Float32(58.9),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("lesha.mtv"),
|
|
||||||
Port: Int32(8002),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalText(t *testing.T) {
|
|
||||||
for i, test := range unMarshalTextTests {
|
|
||||||
pb := new(MyMessage)
|
|
||||||
err := UnmarshalText(test.in, pb)
|
|
||||||
if test.err == "" {
|
|
||||||
// We don't expect failure.
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test %d: Unexpected error: %v", i, err)
|
|
||||||
} else if !reflect.DeepEqual(pb, test.out) {
|
|
||||||
t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
|
|
||||||
i, pb, test.out)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We do expect failure.
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
|
|
||||||
} else if err.Error() != test.err {
|
|
||||||
t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
|
|
||||||
i, err.Error(), test.err)
|
|
||||||
} else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
|
|
||||||
t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
|
|
||||||
i, pb, test.out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalTextCustomMessage(t *testing.T) {
|
|
||||||
msg := &textMessage{}
|
|
||||||
if err := UnmarshalText("custom", msg); err != nil {
|
|
||||||
t.Errorf("Unexpected error from custom unmarshal: %v", err)
|
|
||||||
}
|
|
||||||
if UnmarshalText("not custom", msg) == nil {
|
|
||||||
t.Errorf("Didn't get expected error from custom unmarshal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test; this caused a panic.
|
|
||||||
func TestRepeatedEnum(t *testing.T) {
|
|
||||||
pb := new(RepeatedEnum)
|
|
||||||
if err := UnmarshalText("color: RED", pb); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
exp := &RepeatedEnum{
|
|
||||||
Color: []RepeatedEnum_Color{RepeatedEnum_RED},
|
|
||||||
}
|
|
||||||
if !Equal(pb, exp) {
|
|
||||||
t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProto3TextParsing(t *testing.T) {
|
|
||||||
m := new(proto3pb.Message)
|
|
||||||
const in = `name: "Wallace" true_scotsman: true`
|
|
||||||
want := &proto3pb.Message{
|
|
||||||
Name: "Wallace",
|
|
||||||
TrueScotsman: true,
|
|
||||||
}
|
|
||||||
if err := UnmarshalText(in, m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !Equal(m, want) {
|
|
||||||
t.Errorf("\n got %v\nwant %v", m, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapParsing(t *testing.T) {
|
|
||||||
m := new(MessageWithMap)
|
|
||||||
const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
|
|
||||||
`msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
|
|
||||||
`msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
|
|
||||||
`msg_mapping:<value:<f: 5.0>>` + // omitted key
|
|
||||||
`msg_mapping:<key:1>` + // omitted value
|
|
||||||
`byte_mapping:<key:true value:"so be it">` +
|
|
||||||
`byte_mapping:<>` // omitted key and value
|
|
||||||
want := &MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{
|
|
||||||
1: "Beatles",
|
|
||||||
1234: "Feist",
|
|
||||||
},
|
|
||||||
MsgMapping: map[int64]*FloatingPoint{
|
|
||||||
-4: {F: Float64(2.0)},
|
|
||||||
-2: {F: Float64(4.0)},
|
|
||||||
0: {F: Float64(5.0)},
|
|
||||||
1: nil,
|
|
||||||
},
|
|
||||||
ByteMapping: map[bool][]byte{
|
|
||||||
false: nil,
|
|
||||||
true: []byte("so be it"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := UnmarshalText(in, m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !Equal(m, want) {
|
|
||||||
t.Errorf("\n got %v\nwant %v", m, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOneofParsing(t *testing.T) {
|
|
||||||
const in = `name:"Shrek"`
|
|
||||||
m := new(Communique)
|
|
||||||
want := &Communique{Union: &Communique_Name{"Shrek"}}
|
|
||||||
if err := UnmarshalText(in, m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !Equal(m, want) {
|
|
||||||
t.Errorf("\n got %v\nwant %v", m, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
const inOverwrite = `name:"Shrek" number:42`
|
|
||||||
m = new(Communique)
|
|
||||||
testErr := "line 1.13: field 'number' would overwrite already parsed oneof 'Union'"
|
|
||||||
if err := UnmarshalText(inOverwrite, m); err == nil {
|
|
||||||
t.Errorf("TestOneofParsing: Didn't get expected error: %v", testErr)
|
|
||||||
} else if err.Error() != testErr {
|
|
||||||
t.Errorf("TestOneofParsing: Incorrect error.\nHave: %v\nWant: %v",
|
|
||||||
err.Error(), testErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var benchInput string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
benchInput = "count: 4\n"
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
benchInput += "pet: \"fido\"\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check it is valid input.
|
|
||||||
pb := new(MyMessage)
|
|
||||||
err := UnmarshalText(benchInput, pb)
|
|
||||||
if err != nil {
|
|
||||||
panic("Bad benchmark input: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkUnmarshalText(b *testing.B) {
|
|
||||||
pb := new(MyMessage)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
UnmarshalText(benchInput, pb)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(benchInput)))
|
|
||||||
}
|
|
|
@ -1,474 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// textMessage implements the methods that allow it to marshal and unmarshal
|
|
||||||
// itself as text.
|
|
||||||
type textMessage struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*textMessage) MarshalText() ([]byte, error) {
|
|
||||||
return []byte("custom"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*textMessage) UnmarshalText(bytes []byte) error {
|
|
||||||
if string(bytes) != "custom" {
|
|
||||||
return errors.New("expected 'custom'")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*textMessage) Reset() {}
|
|
||||||
func (*textMessage) String() string { return "" }
|
|
||||||
func (*textMessage) ProtoMessage() {}
|
|
||||||
|
|
||||||
func newTestMessage() *pb.MyMessage {
|
|
||||||
msg := &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
Quote: proto.String(`"I didn't want to go."`),
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("footrest.syd"),
|
|
||||||
Port: proto.Int32(7001),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Key: proto.Int64(0xdeadbeef),
|
|
||||||
Value: []byte{1, 65, 7, 12},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Weight: proto.Float32(6.022),
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("lesha.mtv"),
|
|
||||||
Port: proto.Int32(8002),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Bikeshed: pb.MyMessage_BLUE.Enum(),
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(8),
|
|
||||||
},
|
|
||||||
// One normally wouldn't do this.
|
|
||||||
// This is an undeclared tag 13, as a varint (wire type 0) with value 4.
|
|
||||||
XXX_unrecognized: []byte{13<<3 | 0, 4},
|
|
||||||
}
|
|
||||||
ext := &pb.Ext{
|
|
||||||
Data: proto.String("Big gobs for big rats"),
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
greetings := []string{"adg", "easy", "cow"}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an unknown extension. We marshal a pb.Ext, and fake the ID.
|
|
||||||
b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
|
|
||||||
proto.SetRawExtension(msg, 201, b)
|
|
||||||
|
|
||||||
// Extensions can be plain fields, too, so let's test that.
|
|
||||||
b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
|
|
||||||
proto.SetRawExtension(msg, 202, b)
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = `count: 42
|
|
||||||
name: "Dave"
|
|
||||||
quote: "\"I didn't want to go.\""
|
|
||||||
pet: "bunny"
|
|
||||||
pet: "kitty"
|
|
||||||
pet: "horsey"
|
|
||||||
inner: <
|
|
||||||
host: "footrest.syd"
|
|
||||||
port: 7001
|
|
||||||
connected: true
|
|
||||||
>
|
|
||||||
others: <
|
|
||||||
key: 3735928559
|
|
||||||
value: "\001A\007\014"
|
|
||||||
>
|
|
||||||
others: <
|
|
||||||
weight: 6.022
|
|
||||||
inner: <
|
|
||||||
host: "lesha.mtv"
|
|
||||||
port: 8002
|
|
||||||
>
|
|
||||||
>
|
|
||||||
bikeshed: BLUE
|
|
||||||
SomeGroup {
|
|
||||||
group_field: 8
|
|
||||||
}
|
|
||||||
/* 2 unknown bytes */
|
|
||||||
13: 4
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "Big gobs for big rats"
|
|
||||||
>
|
|
||||||
[testdata.greeting]: "adg"
|
|
||||||
[testdata.greeting]: "easy"
|
|
||||||
[testdata.greeting]: "cow"
|
|
||||||
/* 13 unknown bytes */
|
|
||||||
201: "\t3G skiing"
|
|
||||||
/* 3 unknown bytes */
|
|
||||||
202: 19
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestMarshalText(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := proto.MarshalText(buf, newTestMessage()); err != nil {
|
|
||||||
t.Fatalf("proto.MarshalText: %v", err)
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if s != text {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalTextCustomMessage(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := proto.MarshalText(buf, &textMessage{}); err != nil {
|
|
||||||
t.Fatalf("proto.MarshalText: %v", err)
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if s != "custom" {
|
|
||||||
t.Errorf("Got %q, expected %q", s, "custom")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestMarshalTextNil(t *testing.T) {
|
|
||||||
want := "<nil>"
|
|
||||||
tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
|
|
||||||
for i, test := range tests {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := proto.MarshalText(buf, test); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if got := buf.String(); got != want {
|
|
||||||
t.Errorf("%d: got %q want %q", i, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalTextUnknownEnum(t *testing.T) {
|
|
||||||
// The Color enum only specifies values 0-2.
|
|
||||||
m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
|
|
||||||
got := m.String()
|
|
||||||
const want = `bikeshed:3 `
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("\n got %q\nwant %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTextOneof(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
m proto.Message
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// zero message
|
|
||||||
{&pb.Communique{}, ``},
|
|
||||||
// scalar field
|
|
||||||
{&pb.Communique{Union: &pb.Communique_Number{4}}, `number:4`},
|
|
||||||
// message field
|
|
||||||
{&pb.Communique{Union: &pb.Communique_Msg{
|
|
||||||
&pb.Strings{StringField: proto.String("why hello!")},
|
|
||||||
}}, `msg:<string_field:"why hello!" >`},
|
|
||||||
// bad oneof (should not panic)
|
|
||||||
{&pb.Communique{Union: &pb.Communique_Msg{nil}}, `msg:/* nil */`},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
got := strings.TrimSpace(test.m.String())
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("\n got %s\nwant %s", got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarshalTextBuffered(b *testing.B) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
m := newTestMessage()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
buf.Reset()
|
|
||||||
proto.MarshalText(buf, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarshalTextUnbuffered(b *testing.B) {
|
|
||||||
w := ioutil.Discard
|
|
||||||
m := newTestMessage()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
proto.MarshalText(w, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compact(src string) string {
|
|
||||||
// s/[ \n]+/ /g; s/ $//;
|
|
||||||
dst := make([]byte, len(src))
|
|
||||||
space, comment := false, false
|
|
||||||
j := 0
|
|
||||||
for i := 0; i < len(src); i++ {
|
|
||||||
if strings.HasPrefix(src[i:], "/*") {
|
|
||||||
comment = true
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if comment && strings.HasPrefix(src[i:], "*/") {
|
|
||||||
comment = false
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if comment {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c := src[i]
|
|
||||||
if c == ' ' || c == '\n' {
|
|
||||||
space = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
if c == '{' {
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
if space {
|
|
||||||
dst[j] = ' '
|
|
||||||
j++
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
dst[j] = c
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
if space {
|
|
||||||
dst[j] = ' '
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
return string(dst[0:j])
|
|
||||||
}
|
|
||||||
|
|
||||||
var compactText = compact(text)
|
|
||||||
|
|
||||||
func TestCompactText(t *testing.T) {
|
|
||||||
s := proto.CompactTextString(newTestMessage())
|
|
||||||
if s != compactText {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringEscaping(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
in *pb.Strings
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// Test data from C++ test (TextFormatTest.StringEscape).
|
|
||||||
// Single divergence: we don't escape apostrophes.
|
|
||||||
&pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
|
|
||||||
"string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Test data from the same C++ test.
|
|
||||||
&pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
|
|
||||||
"string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Some UTF-8.
|
|
||||||
&pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
|
|
||||||
`string_field: "\000\001\377\201"` + "\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := proto.MarshalText(&buf, tc.in); err != nil {
|
|
||||||
t.Errorf("proto.MarsalText: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if s != tc.out {
|
|
||||||
t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check round-trip.
|
|
||||||
pb := new(pb.Strings)
|
|
||||||
if err := proto.UnmarshalText(s, pb); err != nil {
|
|
||||||
t.Errorf("#%d: UnmarshalText: %v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !proto.Equal(pb, tc.in) {
|
|
||||||
t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A limitedWriter accepts some output before it fails.
|
|
||||||
// This is a proxy for something like a nearly-full or imminently-failing disk,
|
|
||||||
// or a network connection that is about to die.
|
|
||||||
type limitedWriter struct {
|
|
||||||
b bytes.Buffer
|
|
||||||
limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
var outOfSpace = errors.New("proto: insufficient space")
|
|
||||||
|
|
||||||
func (w *limitedWriter) Write(p []byte) (n int, err error) {
|
|
||||||
var avail = w.limit - w.b.Len()
|
|
||||||
if avail <= 0 {
|
|
||||||
return 0, outOfSpace
|
|
||||||
}
|
|
||||||
if len(p) <= avail {
|
|
||||||
return w.b.Write(p)
|
|
||||||
}
|
|
||||||
n, _ = w.b.Write(p[:avail])
|
|
||||||
return n, outOfSpace
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalTextFailing(t *testing.T) {
|
|
||||||
// Try lots of different sizes to exercise more error code-paths.
|
|
||||||
for lim := 0; lim < len(text); lim++ {
|
|
||||||
buf := new(limitedWriter)
|
|
||||||
buf.limit = lim
|
|
||||||
err := proto.MarshalText(buf, newTestMessage())
|
|
||||||
// We expect a certain error, but also some partial results in the buffer.
|
|
||||||
if err != outOfSpace {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
|
|
||||||
}
|
|
||||||
s := buf.b.String()
|
|
||||||
x := text[:buf.limit]
|
|
||||||
if s != x {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFloats(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
f float64
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{0, "0"},
|
|
||||||
{4.7, "4.7"},
|
|
||||||
{math.Inf(1), "inf"},
|
|
||||||
{math.Inf(-1), "-inf"},
|
|
||||||
{math.NaN(), "nan"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
msg := &pb.FloatingPoint{F: &test.f}
|
|
||||||
got := strings.TrimSpace(msg.String())
|
|
||||||
want := `f:` + test.want
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("f=%f: got %q, want %q", test.f, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRepeatedNilText(t *testing.T) {
|
|
||||||
m := &pb.MessageList{
|
|
||||||
Message: []*pb.MessageList_Message{
|
|
||||||
nil,
|
|
||||||
&pb.MessageList_Message{
|
|
||||||
Name: proto.String("Horse"),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
want := `Message <nil>
|
|
||||||
Message {
|
|
||||||
name: "Horse"
|
|
||||||
}
|
|
||||||
Message <nil>
|
|
||||||
`
|
|
||||||
if s := proto.MarshalTextString(m); s != want {
|
|
||||||
t.Errorf(" got: %s\nwant: %s", s, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProto3Text(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
m proto.Message
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// zero message
|
|
||||||
{&proto3pb.Message{}, ``},
|
|
||||||
// zero message except for an empty byte slice
|
|
||||||
{&proto3pb.Message{Data: []byte{}}, ``},
|
|
||||||
// trivial case
|
|
||||||
{&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
|
|
||||||
// empty map
|
|
||||||
{&pb.MessageWithMap{}, ``},
|
|
||||||
// non-empty map; map format is the same as a repeated struct,
|
|
||||||
// and they are sorted by key (numerically for numeric keys).
|
|
||||||
{
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{
|
|
||||||
-1: "Negatory",
|
|
||||||
7: "Lucky",
|
|
||||||
1234: "Feist",
|
|
||||||
6345789: "Otis",
|
|
||||||
}},
|
|
||||||
`name_mapping:<key:-1 value:"Negatory" > ` +
|
|
||||||
`name_mapping:<key:7 value:"Lucky" > ` +
|
|
||||||
`name_mapping:<key:1234 value:"Feist" > ` +
|
|
||||||
`name_mapping:<key:6345789 value:"Otis" >`,
|
|
||||||
},
|
|
||||||
// map with nil value; not well-defined, but we shouldn't crash
|
|
||||||
{
|
|
||||||
&pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
|
|
||||||
`msg_mapping:<key:7 >`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
got := strings.TrimSpace(test.m.String())
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("\n got %s\nwant %s", got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
// Code generated by protoc-gen-go.
|
|
||||||
// source: github.com/golang/protobuf/ptypes/any/any.proto
|
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package any is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
github.com/golang/protobuf/ptypes/any/any.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
Any
|
|
||||||
*/
|
|
||||||
package any
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
|
||||||
|
|
||||||
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
|
||||||
// URL that describes the type of the serialized message.
|
|
||||||
//
|
|
||||||
// Protobuf library provides support to pack/unpack Any values in the form
|
|
||||||
// of utility functions or additional generated methods of the Any type.
|
|
||||||
//
|
|
||||||
// Example 1: Pack and unpack a message in C++.
|
|
||||||
//
|
|
||||||
// Foo foo = ...;
|
|
||||||
// Any any;
|
|
||||||
// any.PackFrom(foo);
|
|
||||||
// ...
|
|
||||||
// if (any.UnpackTo(&foo)) {
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Example 2: Pack and unpack a message in Java.
|
|
||||||
//
|
|
||||||
// Foo foo = ...;
|
|
||||||
// Any any = Any.pack(foo);
|
|
||||||
// ...
|
|
||||||
// if (any.is(Foo.class)) {
|
|
||||||
// foo = any.unpack(Foo.class);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Example 3: Pack and unpack a message in Python.
|
|
||||||
//
|
|
||||||
// foo = Foo(...)
|
|
||||||
// any = Any()
|
|
||||||
// any.Pack(foo)
|
|
||||||
// ...
|
|
||||||
// if any.Is(Foo.DESCRIPTOR):
|
|
||||||
// any.Unpack(foo)
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// The pack methods provided by protobuf library will by default use
|
|
||||||
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
|
|
||||||
// methods only use the fully qualified type name after the last '/'
|
|
||||||
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
|
|
||||||
// name "y.z".
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// JSON
|
|
||||||
// ====
|
|
||||||
// The JSON representation of an `Any` value uses the regular
|
|
||||||
// representation of the deserialized, embedded message, with an
|
|
||||||
// additional field `@type` which contains the type URL. Example:
|
|
||||||
//
|
|
||||||
// package google.profile;
|
|
||||||
// message Person {
|
|
||||||
// string first_name = 1;
|
|
||||||
// string last_name = 2;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// "@type": "type.googleapis.com/google.profile.Person",
|
|
||||||
// "firstName": <string>,
|
|
||||||
// "lastName": <string>
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// If the embedded message type is well-known and has a custom JSON
|
|
||||||
// representation, that representation will be embedded adding a field
|
|
||||||
// `value` which holds the custom JSON in addition to the `@type`
|
|
||||||
// field. Example (for message [google.protobuf.Duration][]):
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// "@type": "type.googleapis.com/google.protobuf.Duration",
|
|
||||||
// "value": "1.212s"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
type Any struct {
|
|
||||||
// A URL/resource name whose content describes the type of the
|
|
||||||
// serialized protocol buffer message.
|
|
||||||
//
|
|
||||||
// For URLs which use the scheme `http`, `https`, or no scheme, the
|
|
||||||
// following restrictions and interpretations apply:
|
|
||||||
//
|
|
||||||
// * If no scheme is provided, `https` is assumed.
|
|
||||||
// * The last segment of the URL's path must represent the fully
|
|
||||||
// qualified name of the type (as in `path/google.protobuf.Duration`).
|
|
||||||
// The name should be in a canonical form (e.g., leading "." is
|
|
||||||
// not accepted).
|
|
||||||
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
|
||||||
// value in binary format, or produce an error.
|
|
||||||
// * Applications are allowed to cache lookup results based on the
|
|
||||||
// URL, or have them precompiled into a binary to avoid any
|
|
||||||
// lookup. Therefore, binary compatibility needs to be preserved
|
|
||||||
// on changes to types. (Use versioned type names to manage
|
|
||||||
// breaking changes.)
|
|
||||||
//
|
|
||||||
// Schemes other than `http`, `https` (or the empty scheme) might be
|
|
||||||
// used with implementation specific semantics.
|
|
||||||
//
|
|
||||||
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"`
|
|
||||||
// Must be a valid serialized protocol buffer of the above specified type.
|
|
||||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Any) Reset() { *m = Any{} }
|
|
||||||
func (m *Any) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Any) ProtoMessage() {}
|
|
||||||
func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
|
||||||
func (*Any) XXX_WellKnownType() string { return "Any" }
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("github.com/golang/protobuf/ptypes/any/any.proto", fileDescriptor0) }
|
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
|
||||||
// 187 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xcf, 0x2c, 0xc9,
|
|
||||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
|
|
||||||
0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0xcc,
|
|
||||||
0xab, 0x04, 0x61, 0x3d, 0xb0, 0xb8, 0x10, 0x7f, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x1e, 0x4c,
|
|
||||||
0x95, 0x92, 0x19, 0x17, 0xb3, 0x63, 0x5e, 0xa5, 0x90, 0x24, 0x17, 0x07, 0x48, 0x79, 0x7c, 0x69,
|
|
||||||
0x51, 0x8e, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x3b, 0x88, 0x1f, 0x5a, 0x94, 0x23, 0x24,
|
|
||||||
0xc2, 0xc5, 0x5a, 0x96, 0x98, 0x53, 0x9a, 0x2a, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0xe1,
|
|
||||||
0x38, 0x15, 0x71, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe7, 0xc4, 0xe1, 0x98, 0x57, 0x19,
|
|
||||||
0x00, 0xe2, 0x04, 0x30, 0x46, 0xa9, 0x12, 0xe5, 0xb8, 0x05, 0x8c, 0x8c, 0x8b, 0x98, 0x98, 0xdd,
|
|
||||||
0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x4c, 0x0b, 0x80, 0xaa, 0xd2, 0x0b, 0x4f, 0xcd, 0xc9,
|
|
||||||
0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0xa9, 0x4e, 0x62, 0x03, 0x6b, 0x37, 0x06, 0x04, 0x00,
|
|
||||||
0x00, 0xff, 0xff, 0xc6, 0x4d, 0x03, 0x23, 0xf6, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
// Protocol Buffers - Google's data interchange format
|
|
||||||
// Copyright 2008 Google Inc. All rights reserved.
|
|
||||||
// https://developers.google.com/protocol-buffers/
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package google.protobuf;
|
|
||||||
|
|
||||||
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
|
||||||
option go_package = "github.com/golang/protobuf/ptypes/any";
|
|
||||||
option java_package = "com.google.protobuf";
|
|
||||||
option java_outer_classname = "AnyProto";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
option java_generate_equals_and_hash = true;
|
|
||||||
option objc_class_prefix = "GPB";
|
|
||||||
|
|
||||||
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
|
||||||
// URL that describes the type of the serialized message.
|
|
||||||
//
|
|
||||||
// Protobuf library provides support to pack/unpack Any values in the form
|
|
||||||
// of utility functions or additional generated methods of the Any type.
|
|
||||||
//
|
|
||||||
// Example 1: Pack and unpack a message in C++.
|
|
||||||
//
|
|
||||||
// Foo foo = ...;
|
|
||||||
// Any any;
|
|
||||||
// any.PackFrom(foo);
|
|
||||||
// ...
|
|
||||||
// if (any.UnpackTo(&foo)) {
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Example 2: Pack and unpack a message in Java.
|
|
||||||
//
|
|
||||||
// Foo foo = ...;
|
|
||||||
// Any any = Any.pack(foo);
|
|
||||||
// ...
|
|
||||||
// if (any.is(Foo.class)) {
|
|
||||||
// foo = any.unpack(Foo.class);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Example 3: Pack and unpack a message in Python.
|
|
||||||
//
|
|
||||||
// foo = Foo(...)
|
|
||||||
// any = Any()
|
|
||||||
// any.Pack(foo)
|
|
||||||
// ...
|
|
||||||
// if any.Is(Foo.DESCRIPTOR):
|
|
||||||
// any.Unpack(foo)
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// The pack methods provided by protobuf library will by default use
|
|
||||||
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
|
|
||||||
// methods only use the fully qualified type name after the last '/'
|
|
||||||
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
|
|
||||||
// name "y.z".
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// JSON
|
|
||||||
// ====
|
|
||||||
// The JSON representation of an `Any` value uses the regular
|
|
||||||
// representation of the deserialized, embedded message, with an
|
|
||||||
// additional field `@type` which contains the type URL. Example:
|
|
||||||
//
|
|
||||||
// package google.profile;
|
|
||||||
// message Person {
|
|
||||||
// string first_name = 1;
|
|
||||||
// string last_name = 2;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// "@type": "type.googleapis.com/google.profile.Person",
|
|
||||||
// "firstName": <string>,
|
|
||||||
// "lastName": <string>
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// If the embedded message type is well-known and has a custom JSON
|
|
||||||
// representation, that representation will be embedded adding a field
|
|
||||||
// `value` which holds the custom JSON in addition to the `@type`
|
|
||||||
// field. Example (for message [google.protobuf.Duration][]):
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// "@type": "type.googleapis.com/google.protobuf.Duration",
|
|
||||||
// "value": "1.212s"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
message Any {
|
|
||||||
// A URL/resource name whose content describes the type of the
|
|
||||||
// serialized protocol buffer message.
|
|
||||||
//
|
|
||||||
// For URLs which use the scheme `http`, `https`, or no scheme, the
|
|
||||||
// following restrictions and interpretations apply:
|
|
||||||
//
|
|
||||||
// * If no scheme is provided, `https` is assumed.
|
|
||||||
// * The last segment of the URL's path must represent the fully
|
|
||||||
// qualified name of the type (as in `path/google.protobuf.Duration`).
|
|
||||||
// The name should be in a canonical form (e.g., leading "." is
|
|
||||||
// not accepted).
|
|
||||||
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
|
||||||
// value in binary format, or produce an error.
|
|
||||||
// * Applications are allowed to cache lookup results based on the
|
|
||||||
// URL, or have them precompiled into a binary to avoid any
|
|
||||||
// lookup. Therefore, binary compatibility needs to be preserved
|
|
||||||
// on changes to types. (Use versioned type names to manage
|
|
||||||
// breaking changes.)
|
|
||||||
//
|
|
||||||
// Schemes other than `http`, `https` (or the empty scheme) might be
|
|
||||||
// used with implementation specific semantics.
|
|
||||||
//
|
|
||||||
string type_url = 1;
|
|
||||||
|
|
||||||
// Must be a valid serialized protocol buffer of the above specified type.
|
|
||||||
bytes value = 2;
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
// Code generated by protoc-gen-go.
|
|
||||||
// source: google/rpc/status.proto
|
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package status is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
google/rpc/status.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
Status
|
|
||||||
*/
|
|
||||||
package status
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
import google_protobuf "github.com/golang/protobuf/ptypes/any"
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
|
||||||
|
|
||||||
// The `Status` type defines a logical error model that is suitable for different
|
|
||||||
// programming environments, including REST APIs and RPC APIs. It is used by
|
|
||||||
// [gRPC](https://github.com/grpc). The error model is designed to be:
|
|
||||||
//
|
|
||||||
// - Simple to use and understand for most users
|
|
||||||
// - Flexible enough to meet unexpected needs
|
|
||||||
//
|
|
||||||
// # Overview
|
|
||||||
//
|
|
||||||
// The `Status` message contains three pieces of data: error code, error message,
|
|
||||||
// and error details. The error code should be an enum value of
|
|
||||||
// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The
|
|
||||||
// error message should be a developer-facing English message that helps
|
|
||||||
// developers *understand* and *resolve* the error. If a localized user-facing
|
|
||||||
// error message is needed, put the localized message in the error details or
|
|
||||||
// localize it in the client. The optional error details may contain arbitrary
|
|
||||||
// information about the error. There is a predefined set of error detail types
|
|
||||||
// in the package `google.rpc` which can be used for common error conditions.
|
|
||||||
//
|
|
||||||
// # Language mapping
|
|
||||||
//
|
|
||||||
// The `Status` message is the logical representation of the error model, but it
|
|
||||||
// is not necessarily the actual wire format. When the `Status` message is
|
|
||||||
// exposed in different client libraries and different wire protocols, it can be
|
|
||||||
// mapped differently. For example, it will likely be mapped to some exceptions
|
|
||||||
// in Java, but more likely mapped to some error codes in C.
|
|
||||||
//
|
|
||||||
// # Other uses
|
|
||||||
//
|
|
||||||
// The error model and the `Status` message can be used in a variety of
|
|
||||||
// environments, either with or without APIs, to provide a
|
|
||||||
// consistent developer experience across different environments.
|
|
||||||
//
|
|
||||||
// Example uses of this error model include:
|
|
||||||
//
|
|
||||||
// - Partial errors. If a service needs to return partial errors to the client,
|
|
||||||
// it may embed the `Status` in the normal response to indicate the partial
|
|
||||||
// errors.
|
|
||||||
//
|
|
||||||
// - Workflow errors. A typical workflow has multiple steps. Each step may
|
|
||||||
// have a `Status` message for error reporting purpose.
|
|
||||||
//
|
|
||||||
// - Batch operations. If a client uses batch request and batch response, the
|
|
||||||
// `Status` message should be used directly inside batch response, one for
|
|
||||||
// each error sub-response.
|
|
||||||
//
|
|
||||||
// - Asynchronous operations. If an API call embeds asynchronous operation
|
|
||||||
// results in its response, the status of those operations should be
|
|
||||||
// represented directly using the `Status` message.
|
|
||||||
//
|
|
||||||
// - Logging. If some API errors are stored in logs, the message `Status` could
|
|
||||||
// be used directly after any stripping needed for security/privacy reasons.
|
|
||||||
type Status struct {
|
|
||||||
// The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].
|
|
||||||
Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"`
|
|
||||||
// A developer-facing error message, which should be in English. Any
|
|
||||||
// user-facing error message should be localized and sent in the
|
|
||||||
// [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client.
|
|
||||||
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
|
|
||||||
// A list of messages that carry the error details. There will be a
|
|
||||||
// common set of message types for APIs to use.
|
|
||||||
Details []*google_protobuf.Any `protobuf:"bytes,3,rep,name=details" json:"details,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Status) Reset() { *m = Status{} }
|
|
||||||
func (m *Status) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Status) ProtoMessage() {}
|
|
||||||
func (*Status) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
|
||||||
|
|
||||||
func (m *Status) GetCode() int32 {
|
|
||||||
if m != nil {
|
|
||||||
return m.Code
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Status) GetMessage() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Message
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Status) GetDetails() []*google_protobuf.Any {
|
|
||||||
if m != nil {
|
|
||||||
return m.Details
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*Status)(nil), "google.rpc.Status")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("google/rpc/status.proto", fileDescriptor0) }
|
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
|
||||||
// 209 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xcf, 0xcf, 0x4f,
|
|
||||||
0xcf, 0x49, 0xd5, 0x2f, 0x2a, 0x48, 0xd6, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6, 0x2b, 0x28,
|
|
||||||
0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0x48, 0xe8, 0x15, 0x15, 0x24, 0x4b, 0x49, 0x42, 0x15, 0x81,
|
|
||||||
0x65, 0x92, 0x4a, 0xd3, 0xf4, 0x13, 0xf3, 0x2a, 0x21, 0xca, 0x94, 0xd2, 0xb8, 0xd8, 0x82, 0xc1,
|
|
||||||
0xda, 0x84, 0x84, 0xb8, 0x58, 0x92, 0xf3, 0x53, 0x52, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x83,
|
|
||||||
0xc0, 0x6c, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0xf4, 0x54, 0x09, 0x26, 0x05,
|
|
||||||
0x46, 0x0d, 0xce, 0x20, 0x18, 0x57, 0x48, 0x8f, 0x8b, 0x3d, 0x25, 0xb5, 0x24, 0x31, 0x33, 0xa7,
|
|
||||||
0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x6a, 0x21, 0xcc, 0x12, 0x3d, 0xc7,
|
|
||||||
0xbc, 0xca, 0x20, 0x98, 0x22, 0xa7, 0x38, 0x2e, 0xbe, 0xe4, 0xfc, 0x5c, 0x3d, 0x84, 0xa3, 0x9c,
|
|
||||||
0xb8, 0x21, 0xf6, 0x06, 0x80, 0x94, 0x07, 0x30, 0x46, 0x99, 0x43, 0xa5, 0xd2, 0xf3, 0x73, 0x12,
|
|
||||||
0xf3, 0xd2, 0xf5, 0xf2, 0x8b, 0xd2, 0xf5, 0xd3, 0x53, 0xf3, 0xc0, 0x86, 0xe9, 0x43, 0xa4, 0x12,
|
|
||||||
0x0b, 0x32, 0x8b, 0x91, 0xfc, 0x69, 0x0d, 0xa1, 0x16, 0x31, 0x31, 0x07, 0x05, 0x38, 0x27, 0xb1,
|
|
||||||
0x81, 0x55, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x53, 0xf0, 0x7c, 0x10, 0x01, 0x00,
|
|
||||||
0x00,
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
// ActivityService handles communication with the activity related
|
|
||||||
// methods of the GitHub API.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/
|
|
||||||
type ActivityService service
|
|
||||||
|
|
||||||
// FeedLink represents a link to a related resource.
|
|
||||||
type FeedLink struct {
|
|
||||||
HRef *string `json:"href,omitempty"`
|
|
||||||
Type *string `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Feeds represents timeline resources in Atom format.
|
|
||||||
type Feeds struct {
|
|
||||||
TimelineURL *string `json:"timeline_url,omitempty"`
|
|
||||||
UserURL *string `json:"user_url,omitempty"`
|
|
||||||
CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"`
|
|
||||||
CurrentUserURL *string `json:"current_user_url,omitempty"`
|
|
||||||
CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"`
|
|
||||||
CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"`
|
|
||||||
CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"`
|
|
||||||
Links *struct {
|
|
||||||
Timeline *FeedLink `json:"timeline,omitempty"`
|
|
||||||
User *FeedLink `json:"user,omitempty"`
|
|
||||||
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"`
|
|
||||||
CurrentUser *FeedLink `json:"current_user,omitempty"`
|
|
||||||
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"`
|
|
||||||
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"`
|
|
||||||
CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"`
|
|
||||||
} `json:"_links,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListFeeds lists all the feeds available to the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub provides several timeline resources in Atom format:
|
|
||||||
// Timeline: The GitHub global public timeline
|
|
||||||
// User: The public timeline for any user, using URI template
|
|
||||||
// Current user public: The public timeline for the authenticated user
|
|
||||||
// Current user: The private timeline for the authenticated user
|
|
||||||
// Current user actor: The private timeline for activity created by the
|
|
||||||
// authenticated user
|
|
||||||
// Current user organizations: The private timeline for the organizations
|
|
||||||
// the authenticated user is a member of.
|
|
||||||
//
|
|
||||||
// Note: Private feeds are only returned when authenticating via Basic Auth
|
|
||||||
// since current feed URIs use the older, non revocable auth tokens.
|
|
||||||
func (s *ActivityService) ListFeeds(ctx context.Context) (*Feeds, *Response, error) {
|
|
||||||
req, err := s.client.NewRequest("GET", "feeds", nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f := &Feeds{}
|
|
||||||
resp, err := s.client.Do(ctx, req, f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return f, resp, nil
|
|
||||||
}
|
|
|
@ -1,318 +0,0 @@
|
||||||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Event represents a GitHub event.
|
|
||||||
type Event struct {
|
|
||||||
Type *string `json:"type,omitempty"`
|
|
||||||
Public *bool `json:"public"`
|
|
||||||
RawPayload *json.RawMessage `json:"payload,omitempty"`
|
|
||||||
Repo *Repository `json:"repo,omitempty"`
|
|
||||||
Actor *User `json:"actor,omitempty"`
|
|
||||||
Org *Organization `json:"org,omitempty"`
|
|
||||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
|
||||||
ID *string `json:"id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Event) String() string {
|
|
||||||
return Stringify(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsePayload parses the event payload. For recognized event types,
|
|
||||||
// a value of the corresponding struct type will be returned.
|
|
||||||
func (e *Event) ParsePayload() (payload interface{}, err error) {
|
|
||||||
switch *e.Type {
|
|
||||||
case "CommitCommentEvent":
|
|
||||||
payload = &CommitCommentEvent{}
|
|
||||||
case "CreateEvent":
|
|
||||||
payload = &CreateEvent{}
|
|
||||||
case "DeleteEvent":
|
|
||||||
payload = &DeleteEvent{}
|
|
||||||
case "DeploymentEvent":
|
|
||||||
payload = &DeploymentEvent{}
|
|
||||||
case "DeploymentStatusEvent":
|
|
||||||
payload = &DeploymentStatusEvent{}
|
|
||||||
case "ForkEvent":
|
|
||||||
payload = &ForkEvent{}
|
|
||||||
case "GollumEvent":
|
|
||||||
payload = &GollumEvent{}
|
|
||||||
case "IntegrationInstallationEvent":
|
|
||||||
payload = &IntegrationInstallationEvent{}
|
|
||||||
case "IntegrationInstallationRepositoriesEvent":
|
|
||||||
payload = &IntegrationInstallationRepositoriesEvent{}
|
|
||||||
case "IssueCommentEvent":
|
|
||||||
payload = &IssueCommentEvent{}
|
|
||||||
case "IssuesEvent":
|
|
||||||
payload = &IssuesEvent{}
|
|
||||||
case "LabelEvent":
|
|
||||||
payload = &LabelEvent{}
|
|
||||||
case "MemberEvent":
|
|
||||||
payload = &MemberEvent{}
|
|
||||||
case "MembershipEvent":
|
|
||||||
payload = &MembershipEvent{}
|
|
||||||
case "MilestoneEvent":
|
|
||||||
payload = &MilestoneEvent{}
|
|
||||||
case "OrganizationEvent":
|
|
||||||
payload = &OrganizationEvent{}
|
|
||||||
case "PageBuildEvent":
|
|
||||||
payload = &PageBuildEvent{}
|
|
||||||
case "PingEvent":
|
|
||||||
payload = &PingEvent{}
|
|
||||||
case "ProjectEvent":
|
|
||||||
payload = &ProjectEvent{}
|
|
||||||
case "ProjectCardEvent":
|
|
||||||
payload = &ProjectCardEvent{}
|
|
||||||
case "ProjectColumnEvent":
|
|
||||||
payload = &ProjectColumnEvent{}
|
|
||||||
case "PublicEvent":
|
|
||||||
payload = &PublicEvent{}
|
|
||||||
case "PullRequestEvent":
|
|
||||||
payload = &PullRequestEvent{}
|
|
||||||
case "PullRequestReviewEvent":
|
|
||||||
payload = &PullRequestReviewEvent{}
|
|
||||||
case "PullRequestReviewCommentEvent":
|
|
||||||
payload = &PullRequestReviewCommentEvent{}
|
|
||||||
case "PushEvent":
|
|
||||||
payload = &PushEvent{}
|
|
||||||
case "ReleaseEvent":
|
|
||||||
payload = &ReleaseEvent{}
|
|
||||||
case "RepositoryEvent":
|
|
||||||
payload = &RepositoryEvent{}
|
|
||||||
case "StatusEvent":
|
|
||||||
payload = &StatusEvent{}
|
|
||||||
case "TeamAddEvent":
|
|
||||||
payload = &TeamAddEvent{}
|
|
||||||
case "WatchEvent":
|
|
||||||
payload = &WatchEvent{}
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(*e.RawPayload, &payload)
|
|
||||||
return payload, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Payload returns the parsed event payload. For recognized event types,
|
|
||||||
// a value of the corresponding struct type will be returned.
|
|
||||||
//
|
|
||||||
// Deprecated: Use ParsePayload instead, which returns an error
|
|
||||||
// rather than panics if JSON unmarshaling raw payload fails.
|
|
||||||
func (e *Event) Payload() (payload interface{}) {
|
|
||||||
var err error
|
|
||||||
payload, err = e.ParsePayload()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return payload
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEvents drinks from the firehose of all public events across GitHub.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events
|
|
||||||
func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
u, err := addOptions("events", opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRepositoryEvents lists events for a repository.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-repository-events
|
|
||||||
func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/events", owner, repo)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListIssueEventsForRepository lists issue events for a repository.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository
|
|
||||||
func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*IssueEvent
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEventsForRepoNetwork lists public events for a network of repositories.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories
|
|
||||||
func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
u := fmt.Sprintf("networks/%v/%v/events", owner, repo)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEventsForOrganization lists public events for an organization.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization
|
|
||||||
func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
u := fmt.Sprintf("orgs/%v/events", org)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEventsPerformedByUser lists the events performed by a user. If publicOnly is
|
|
||||||
// true, only public events will be returned.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user
|
|
||||||
func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
var u string
|
|
||||||
if publicOnly {
|
|
||||||
u = fmt.Sprintf("users/%v/events/public", user)
|
|
||||||
} else {
|
|
||||||
u = fmt.Sprintf("users/%v/events", user)
|
|
||||||
}
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEventsReceivedByUser lists the events received by a user. If publicOnly is
|
|
||||||
// true, only public events will be returned.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received
|
|
||||||
func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
var u string
|
|
||||||
if publicOnly {
|
|
||||||
u = fmt.Sprintf("users/%v/received_events/public", user)
|
|
||||||
} else {
|
|
||||||
u = fmt.Sprintf("users/%v/received_events", user)
|
|
||||||
}
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUserEventsForOrganization provides the user’s organization dashboard. You
|
|
||||||
// must be authenticated as the user to view this.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-an-organization
|
|
||||||
func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opt *ListOptions) ([]*Event, *Response, error) {
|
|
||||||
u := fmt.Sprintf("users/%v/events/orgs/%v", user, org)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var events []*Event
|
|
||||||
resp, err := s.client.Do(ctx, req, &events)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return events, resp, nil
|
|
||||||
}
|
|
|
@ -1,331 +0,0 @@
|
||||||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActivityService_ListEvents(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListEvents(context.Background(), opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activities.ListEvents returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Activities.ListEvents returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListRepositoryEvents(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListRepositoryEvents(context.Background(), "o", "r", opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activities.ListRepositoryEvents returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Activities.ListRepositoryEvents returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListRepositoryEvents_invalidOwner(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListRepositoryEvents(context.Background(), "%", "%", nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListIssueEventsForRepository(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":1},{"id":2}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "o", "r", opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activities.ListIssueEventsForRepository returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*IssueEvent{{ID: Int(1)}, {ID: Int(2)}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Activities.ListIssueEventsForRepository returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListIssueEventsForRepository_invalidOwner(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "%", "%", nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsForRepoNetwork(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/networks/o/r/events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "o", "r", opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activities.ListEventsForRepoNetwork returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Activities.ListEventsForRepoNetwork returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsForRepoNetwork_invalidOwner(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "%", "%", nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsForOrganization(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/orgs/o/events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListEventsForOrganization(context.Background(), "o", opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activities.ListEventsForOrganization returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Activities.ListEventsForOrganization returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsForOrganization_invalidOrg(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListEventsForOrganization(context.Background(), "%", nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "u", false, opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Events.ListPerformedByUser returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/events/public", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
events, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "u", true, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Events.ListPerformedByUser returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsPerformedByUser_invalidUser(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "%", false, nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/received_events", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "u", false, opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Events.ListReceivedByUser returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Events.ListReceivedUser returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsReceivedByUser_publicOnly(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/received_events/public", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
events, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "u", true, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Events.ListReceivedByUser returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Events.ListReceivedByUser returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListEventsReceivedByUser_invalidUser(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "%", false, nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListUserEventsForOrganization(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/events/orgs/o", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 2}
|
|
||||||
events, _, err := client.Activity.ListUserEventsForOrganization(context.Background(), "o", "u", opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activities.ListUserEventsForOrganization returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Event{{ID: String("1")}, {ID: String("2")}}
|
|
||||||
if !reflect.DeepEqual(events, want) {
|
|
||||||
t.Errorf("Activities.ListUserEventsForOrganization returned %+v, want %+v", events, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_EventParsePayload_typed(t *testing.T) {
|
|
||||||
raw := []byte(`{"type": "PushEvent","payload":{"push_id": 1}}`)
|
|
||||||
var event *Event
|
|
||||||
if err := json.Unmarshal(raw, &event); err != nil {
|
|
||||||
t.Fatalf("Unmarshal Event returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &PushEvent{PushID: Int(1)}
|
|
||||||
got, err := event.ParsePayload()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ParsePayload returned unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestEvent_Payload_untyped checks that unrecognized events are parsed to an
|
|
||||||
// interface{} value (instead of being discarded or throwing an error), for
|
|
||||||
// forward compatibility with new event types.
|
|
||||||
func TestActivityService_EventParsePayload_untyped(t *testing.T) {
|
|
||||||
raw := []byte(`{"type": "UnrecognizedEvent","payload":{"field": "val"}}`)
|
|
||||||
var event *Event
|
|
||||||
if err := json.Unmarshal(raw, &event); err != nil {
|
|
||||||
t.Fatalf("Unmarshal Event returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := map[string]interface{}{"field": "val"}
|
|
||||||
got, err := event.ParsePayload()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ParsePayload returned unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_EventParsePayload_installation(t *testing.T) {
|
|
||||||
raw := []byte(`{"type": "PullRequestEvent","payload":{"installation":{"id":1}}}`)
|
|
||||||
var event *Event
|
|
||||||
if err := json.Unmarshal(raw, &event); err != nil {
|
|
||||||
t.Fatalf("Unmarshal Event returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &PullRequestEvent{Installation: &Installation{ID: Int(1)}}
|
|
||||||
got, err := event.ParsePayload()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ParsePayload returned unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
// Copyright 2014 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Notification identifies a GitHub notification for a user.
|
|
||||||
type Notification struct {
|
|
||||||
ID *string `json:"id,omitempty"`
|
|
||||||
Repository *Repository `json:"repository,omitempty"`
|
|
||||||
Subject *NotificationSubject `json:"subject,omitempty"`
|
|
||||||
|
|
||||||
// Reason identifies the event that triggered the notification.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#notification-reasons
|
|
||||||
Reason *string `json:"reason,omitempty"`
|
|
||||||
|
|
||||||
Unread *bool `json:"unread,omitempty"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
|
||||||
LastReadAt *time.Time `json:"last_read_at,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotificationSubject identifies the subject of a notification.
|
|
||||||
type NotificationSubject struct {
|
|
||||||
Title *string `json:"title,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
LatestCommentURL *string `json:"latest_comment_url,omitempty"`
|
|
||||||
Type *string `json:"type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotificationListOptions specifies the optional parameters to the
|
|
||||||
// ActivityService.ListNotifications method.
|
|
||||||
type NotificationListOptions struct {
|
|
||||||
All bool `url:"all,omitempty"`
|
|
||||||
Participating bool `url:"participating,omitempty"`
|
|
||||||
Since time.Time `url:"since,omitempty"`
|
|
||||||
Before time.Time `url:"before,omitempty"`
|
|
||||||
|
|
||||||
ListOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListNotifications lists all notifications for the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications
|
|
||||||
func (s *ActivityService) ListNotifications(ctx context.Context, opt *NotificationListOptions) ([]*Notification, *Response, error) {
|
|
||||||
u := fmt.Sprintf("notifications")
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var notifications []*Notification
|
|
||||||
resp, err := s.client.Do(ctx, req, ¬ifications)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return notifications, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRepositoryNotifications lists all notifications in a given repository
|
|
||||||
// for the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository
|
|
||||||
func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var notifications []*Notification
|
|
||||||
resp, err := s.client.Do(ctx, req, ¬ifications)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return notifications, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type markReadOptions struct {
|
|
||||||
LastReadAt time.Time `json:"last_read_at,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkNotificationsRead marks all notifications up to lastRead as read.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read
|
|
||||||
func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) {
|
|
||||||
opts := &markReadOptions{
|
|
||||||
LastReadAt: lastRead,
|
|
||||||
}
|
|
||||||
req, err := s.client.NewRequest("PUT", "notifications", opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkRepositoryNotificationsRead marks all notifications up to lastRead in
|
|
||||||
// the specified repository as read.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository
|
|
||||||
func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) {
|
|
||||||
opts := &markReadOptions{
|
|
||||||
LastReadAt: lastRead,
|
|
||||||
}
|
|
||||||
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo)
|
|
||||||
req, err := s.client.NewRequest("PUT", u, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetThread gets the specified notification thread.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread
|
|
||||||
func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) {
|
|
||||||
u := fmt.Sprintf("notifications/threads/%v", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
notification := new(Notification)
|
|
||||||
resp, err := s.client.Do(ctx, req, notification)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return notification, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkThreadRead marks the specified thread as read.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read
|
|
||||||
func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("notifications/threads/%v", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetThreadSubscription checks to see if the authenticated user is subscribed
|
|
||||||
// to a thread.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription
|
|
||||||
func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) {
|
|
||||||
u := fmt.Sprintf("notifications/threads/%v/subscription", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sub := new(Subscription)
|
|
||||||
resp, err := s.client.Do(ctx, req, sub)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sub, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetThreadSubscription sets the subscription for the specified thread for the
|
|
||||||
// authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription
|
|
||||||
func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, subscription *Subscription) (*Subscription, *Response, error) {
|
|
||||||
u := fmt.Sprintf("notifications/threads/%v/subscription", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PUT", u, subscription)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sub := new(Subscription)
|
|
||||||
resp, err := s.client.Do(ctx, req, sub)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sub, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteThreadSubscription deletes the subscription for the specified thread
|
|
||||||
// for the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription
|
|
||||||
func (s *ActivityService) DeleteThreadSubscription(ctx context.Context, id string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("notifications/threads/%v/subscription", id)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
|
@ -1,204 +0,0 @@
|
||||||
// Copyright 2014 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActivityService_ListNotification(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"all": "true",
|
|
||||||
"participating": "true",
|
|
||||||
"since": "2006-01-02T15:04:05Z",
|
|
||||||
"before": "2007-03-04T15:04:05Z",
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Fprint(w, `[{"id":"1", "subject":{"title":"t"}}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &NotificationListOptions{
|
|
||||||
All: true,
|
|
||||||
Participating: true,
|
|
||||||
Since: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
|
|
||||||
Before: time.Date(2007, 03, 04, 15, 04, 05, 0, time.UTC),
|
|
||||||
}
|
|
||||||
notifications, _, err := client.Activity.ListNotifications(context.Background(), opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListNotifications returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Notification{{ID: String("1"), Subject: &NotificationSubject{Title: String("t")}}}
|
|
||||||
if !reflect.DeepEqual(notifications, want) {
|
|
||||||
t.Errorf("Activity.ListNotifications returned %+v, want %+v", notifications, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListRepositoryNotification(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `[{"id":"1"}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
notifications, _, err := client.Activity.ListRepositoryNotifications(context.Background(), "o", "r", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListRepositoryNotifications returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Notification{{ID: String("1")}}
|
|
||||||
if !reflect.DeepEqual(notifications, want) {
|
|
||||||
t.Errorf("Activity.ListRepositoryNotifications returned %+v, want %+v", notifications, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_MarkNotificationsRead(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
testHeader(t, r, "Content-Type", "application/json")
|
|
||||||
testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n")
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusResetContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.MarkNotificationsRead(context.Background(), time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.MarkNotificationsRead returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
testHeader(t, r, "Content-Type", "application/json")
|
|
||||||
testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n")
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusResetContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.MarkRepositoryNotificationsRead(context.Background(), "o", "r", time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_GetThread(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `{"id":"1"}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
notification, _, err := client.Activity.GetThread(context.Background(), "1")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.GetThread returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Notification{ID: String("1")}
|
|
||||||
if !reflect.DeepEqual(notification, want) {
|
|
||||||
t.Errorf("Activity.GetThread returned %+v, want %+v", notification, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_MarkThreadRead(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "PATCH")
|
|
||||||
w.WriteHeader(http.StatusResetContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.MarkThreadRead(context.Background(), "1")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.MarkThreadRead returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_GetThreadSubscription(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `{"subscribed":true}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
sub, _, err := client.Activity.GetThreadSubscription(context.Background(), "1")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.GetThreadSubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Subscription{Subscribed: Bool(true)}
|
|
||||||
if !reflect.DeepEqual(sub, want) {
|
|
||||||
t.Errorf("Activity.GetThreadSubscription returned %+v, want %+v", sub, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_SetThreadSubscription(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &Subscription{Subscribed: Bool(true)}
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(Subscription)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(w, `{"ignored":true}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
sub, _, err := client.Activity.SetThreadSubscription(context.Background(), "1", input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.SetThreadSubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Subscription{Ignored: Bool(true)}
|
|
||||||
if !reflect.DeepEqual(sub, want) {
|
|
||||||
t.Errorf("Activity.SetThreadSubscription returned %+v, want %+v", sub, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_DeleteThreadSubscription(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.DeleteThreadSubscription(context.Background(), "1")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.DeleteThreadSubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StarredRepository is returned by ListStarred.
|
|
||||||
type StarredRepository struct {
|
|
||||||
StarredAt *Timestamp `json:"starred_at,omitempty"`
|
|
||||||
Repository *Repository `json:"repo,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stargazer represents a user that has starred a repository.
|
|
||||||
type Stargazer struct {
|
|
||||||
StarredAt *Timestamp `json:"starred_at,omitempty"`
|
|
||||||
User *User `json:"user,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListStargazers lists people who have starred the specified repo.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-stargazers
|
|
||||||
func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches
|
|
||||||
req.Header.Set("Accept", mediaTypeStarringPreview)
|
|
||||||
|
|
||||||
var stargazers []*Stargazer
|
|
||||||
resp, err := s.client.Do(ctx, req, &stargazers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return stargazers, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActivityListStarredOptions specifies the optional parameters to the
|
|
||||||
// ActivityService.ListStarred method.
|
|
||||||
type ActivityListStarredOptions struct {
|
|
||||||
// How to sort the repository list. Possible values are: created, updated,
|
|
||||||
// pushed, full_name. Default is "full_name".
|
|
||||||
Sort string `url:"sort,omitempty"`
|
|
||||||
|
|
||||||
// Direction in which to sort repositories. Possible values are: asc, desc.
|
|
||||||
// Default is "asc" when sort is "full_name", otherwise default is "desc".
|
|
||||||
Direction string `url:"direction,omitempty"`
|
|
||||||
|
|
||||||
ListOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListStarred lists all the repos starred by a user. Passing the empty string
|
|
||||||
// will list the starred repositories for the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred
|
|
||||||
func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) {
|
|
||||||
var u string
|
|
||||||
if user != "" {
|
|
||||||
u = fmt.Sprintf("users/%v/starred", user)
|
|
||||||
} else {
|
|
||||||
u = "user/starred"
|
|
||||||
}
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove custom Accept header when this API fully launches
|
|
||||||
req.Header.Set("Accept", mediaTypeStarringPreview)
|
|
||||||
|
|
||||||
var repos []*StarredRepository
|
|
||||||
resp, err := s.client.Do(ctx, req, &repos)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return repos, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStarred checks if a repository is starred by authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository
|
|
||||||
func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bool, *Response, error) {
|
|
||||||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo)
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, nil, err
|
|
||||||
}
|
|
||||||
resp, err := s.client.Do(ctx, req, nil)
|
|
||||||
starred, err := parseBoolResponse(err)
|
|
||||||
return starred, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Star a repository as the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository
|
|
||||||
func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo)
|
|
||||||
req, err := s.client.NewRequest("PUT", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unstar a repository as the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository
|
|
||||||
func (s *ActivityService) Unstar(ctx context.Context, owner, repo string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActivityService_ListStargazers(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testHeader(t, r, "Accept", mediaTypeStarringPreview)
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","user":{"id":1}}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
stargazers, _, err := client.Activity.ListStargazers(context.Background(), "o", "r", &ListOptions{Page: 2})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListStargazers returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int(1)}}}
|
|
||||||
if !reflect.DeepEqual(stargazers, want) {
|
|
||||||
t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListStarred_authenticatedUser(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testHeader(t, r, "Accept", mediaTypeStarringPreview)
|
|
||||||
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
repos, _, err := client.Activity.ListStarred(context.Background(), "", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListStarred returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int(1)}}}
|
|
||||||
if !reflect.DeepEqual(repos, want) {
|
|
||||||
t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListStarred_specifiedUser(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testHeader(t, r, "Accept", mediaTypeStarringPreview)
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"sort": "created",
|
|
||||||
"direction": "asc",
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":2}}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}}
|
|
||||||
repos, _, err := client.Activity.ListStarred(context.Background(), "u", opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListStarred returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int(2)}}}
|
|
||||||
if !reflect.DeepEqual(repos, want) {
|
|
||||||
t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListStarred_invalidUser(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.ListStarred(context.Background(), "%", nil)
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_IsStarred_hasStar(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
star, _, err := client.Activity.IsStarred(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.IsStarred returned error: %v", err)
|
|
||||||
}
|
|
||||||
if want := true; star != want {
|
|
||||||
t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_IsStarred_noStar(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
})
|
|
||||||
|
|
||||||
star, _, err := client.Activity.IsStarred(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.IsStarred returned error: %v", err)
|
|
||||||
}
|
|
||||||
if want := false; star != want {
|
|
||||||
t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_IsStarred_invalidID(t *testing.T) {
|
|
||||||
_, _, err := client.Activity.IsStarred(context.Background(), "%", "%")
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_Star(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.Star(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.Star returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_Star_invalidID(t *testing.T) {
|
|
||||||
_, err := client.Activity.Star(context.Background(), "%", "%")
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_Unstar(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.Unstar(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.Unstar returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_Unstar_invalidID(t *testing.T) {
|
|
||||||
_, err := client.Activity.Unstar(context.Background(), "%", "%")
|
|
||||||
testURLParseError(t, err)
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
// Copyright 2016 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActivityService_List(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/feeds", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write(feedsJSON)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Activity.ListFeeds(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListFeeds returned error: %v", err)
|
|
||||||
}
|
|
||||||
if want := wantFeeds; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Activity.ListFeeds = %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var feedsJSON = []byte(`{
|
|
||||||
"timeline_url": "https://github.com/timeline",
|
|
||||||
"user_url": "https://github.com/{user}",
|
|
||||||
"current_user_public_url": "https://github.com/defunkt",
|
|
||||||
"current_user_url": "https://github.com/defunkt.private?token=abc123",
|
|
||||||
"current_user_actor_url": "https://github.com/defunkt.private.actor?token=abc123",
|
|
||||||
"current_user_organization_url": "",
|
|
||||||
"current_user_organization_urls": [
|
|
||||||
"https://github.com/organizations/github/defunkt.private.atom?token=abc123"
|
|
||||||
],
|
|
||||||
"_links": {
|
|
||||||
"timeline": {
|
|
||||||
"href": "https://github.com/timeline",
|
|
||||||
"type": "application/atom+xml"
|
|
||||||
},
|
|
||||||
"user": {
|
|
||||||
"href": "https://github.com/{user}",
|
|
||||||
"type": "application/atom+xml"
|
|
||||||
},
|
|
||||||
"current_user_public": {
|
|
||||||
"href": "https://github.com/defunkt",
|
|
||||||
"type": "application/atom+xml"
|
|
||||||
},
|
|
||||||
"current_user": {
|
|
||||||
"href": "https://github.com/defunkt.private?token=abc123",
|
|
||||||
"type": "application/atom+xml"
|
|
||||||
},
|
|
||||||
"current_user_actor": {
|
|
||||||
"href": "https://github.com/defunkt.private.actor?token=abc123",
|
|
||||||
"type": "application/atom+xml"
|
|
||||||
},
|
|
||||||
"current_user_organization": {
|
|
||||||
"href": "",
|
|
||||||
"type": ""
|
|
||||||
},
|
|
||||||
"current_user_organizations": [
|
|
||||||
{
|
|
||||||
"href": "https://github.com/organizations/github/defunkt.private.atom?token=abc123",
|
|
||||||
"type": "application/atom+xml"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var wantFeeds = &Feeds{
|
|
||||||
TimelineURL: String("https://github.com/timeline"),
|
|
||||||
UserURL: String("https://github.com/{user}"),
|
|
||||||
CurrentUserPublicURL: String("https://github.com/defunkt"),
|
|
||||||
CurrentUserURL: String("https://github.com/defunkt.private?token=abc123"),
|
|
||||||
CurrentUserActorURL: String("https://github.com/defunkt.private.actor?token=abc123"),
|
|
||||||
CurrentUserOrganizationURL: String(""),
|
|
||||||
CurrentUserOrganizationURLs: []string{
|
|
||||||
"https://github.com/organizations/github/defunkt.private.atom?token=abc123",
|
|
||||||
},
|
|
||||||
Links: &struct {
|
|
||||||
Timeline *FeedLink `json:"timeline,omitempty"`
|
|
||||||
User *FeedLink `json:"user,omitempty"`
|
|
||||||
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"`
|
|
||||||
CurrentUser *FeedLink `json:"current_user,omitempty"`
|
|
||||||
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"`
|
|
||||||
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"`
|
|
||||||
CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"`
|
|
||||||
}{
|
|
||||||
Timeline: &FeedLink{
|
|
||||||
HRef: String("https://github.com/timeline"),
|
|
||||||
Type: String("application/atom+xml"),
|
|
||||||
},
|
|
||||||
User: &FeedLink{
|
|
||||||
HRef: String("https://github.com/{user}"),
|
|
||||||
Type: String("application/atom+xml"),
|
|
||||||
},
|
|
||||||
CurrentUserPublic: &FeedLink{
|
|
||||||
HRef: String("https://github.com/defunkt"),
|
|
||||||
Type: String("application/atom+xml"),
|
|
||||||
},
|
|
||||||
CurrentUser: &FeedLink{
|
|
||||||
HRef: String("https://github.com/defunkt.private?token=abc123"),
|
|
||||||
Type: String("application/atom+xml"),
|
|
||||||
},
|
|
||||||
CurrentUserActor: &FeedLink{
|
|
||||||
HRef: String("https://github.com/defunkt.private.actor?token=abc123"),
|
|
||||||
Type: String("application/atom+xml"),
|
|
||||||
},
|
|
||||||
CurrentUserOrganization: &FeedLink{
|
|
||||||
HRef: String(""),
|
|
||||||
Type: String(""),
|
|
||||||
},
|
|
||||||
CurrentUserOrganizations: []FeedLink{
|
|
||||||
{
|
|
||||||
HRef: String("https://github.com/organizations/github/defunkt.private.atom?token=abc123"),
|
|
||||||
Type: String("application/atom+xml"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,146 +0,0 @@
|
||||||
// Copyright 2014 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Subscription identifies a repository or thread subscription.
|
|
||||||
type Subscription struct {
|
|
||||||
Subscribed *bool `json:"subscribed,omitempty"`
|
|
||||||
Ignored *bool `json:"ignored,omitempty"`
|
|
||||||
Reason *string `json:"reason,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
|
|
||||||
// only populated for repository subscriptions
|
|
||||||
RepositoryURL *string `json:"repository_url,omitempty"`
|
|
||||||
|
|
||||||
// only populated for thread subscriptions
|
|
||||||
ThreadURL *string `json:"thread_url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListWatchers lists watchers of a particular repo.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-watchers
|
|
||||||
func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo)
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var watchers []*User
|
|
||||||
resp, err := s.client.Do(ctx, req, &watchers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return watchers, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListWatched lists the repositories the specified user is watching. Passing
|
|
||||||
// the empty string will fetch watched repos for the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched
|
|
||||||
func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *ListOptions) ([]*Repository, *Response, error) {
|
|
||||||
var u string
|
|
||||||
if user != "" {
|
|
||||||
u = fmt.Sprintf("users/%v/subscriptions", user)
|
|
||||||
} else {
|
|
||||||
u = "user/subscriptions"
|
|
||||||
}
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var watched []*Repository
|
|
||||||
resp, err := s.client.Do(ctx, req, &watched)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return watched, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRepositorySubscription returns the subscription for the specified
|
|
||||||
// repository for the authenticated user. If the authenticated user is not
|
|
||||||
// watching the repository, a nil Subscription is returned.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription
|
|
||||||
func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, repo string) (*Subscription, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sub := new(Subscription)
|
|
||||||
resp, err := s.client.Do(ctx, req, sub)
|
|
||||||
if err != nil {
|
|
||||||
// if it's just a 404, don't return that as an error
|
|
||||||
_, err = parseBoolResponse(err)
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sub, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRepositorySubscription sets the subscription for the specified repository
|
|
||||||
// for the authenticated user.
|
|
||||||
//
|
|
||||||
// To watch a repository, set subscription.Subscribed to true.
|
|
||||||
// To ignore notifications made within a repository, set subscription.Ignored to true.
|
|
||||||
// To stop watching a repository, use DeleteRepositorySubscription.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription
|
|
||||||
func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, subscription *Subscription) (*Subscription, *Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PUT", u, subscription)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sub := new(Subscription)
|
|
||||||
resp, err := s.client.Do(ctx, req, sub)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sub, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRepositorySubscription deletes the subscription for the specified
|
|
||||||
// repository for the authenticated user.
|
|
||||||
//
|
|
||||||
// This is used to stop watching a repository. To control whether or not to
|
|
||||||
// receive notifications from a repository, use SetRepositorySubscription.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription
|
|
||||||
func (s *ActivityService) DeleteRepositorySubscription(ctx context.Context, owner, repo string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
|
@ -1,184 +0,0 @@
|
||||||
// Copyright 2014 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestActivityService_ListWatchers(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/subscribers", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
|
|
||||||
fmt.Fprint(w, `[{"id":1}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
watchers, _, err := client.Activity.ListWatchers(context.Background(), "o", "r", &ListOptions{Page: 2})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListWatchers returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*User{{ID: Int(1)}}
|
|
||||||
if !reflect.DeepEqual(watchers, want) {
|
|
||||||
t.Errorf("Activity.ListWatchers returned %+v, want %+v", watchers, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListWatched_authenticatedUser(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/user/subscriptions", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":1}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
watched, _, err := client.Activity.ListWatched(context.Background(), "", &ListOptions{Page: 2})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListWatched returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Repository{{ID: Int(1)}}
|
|
||||||
if !reflect.DeepEqual(watched, want) {
|
|
||||||
t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_ListWatched_specifiedUser(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/users/u/subscriptions", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{
|
|
||||||
"page": "2",
|
|
||||||
})
|
|
||||||
fmt.Fprint(w, `[{"id":1}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
watched, _, err := client.Activity.ListWatched(context.Background(), "u", &ListOptions{Page: 2})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.ListWatched returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Repository{{ID: Int(1)}}
|
|
||||||
if !reflect.DeepEqual(watched, want) {
|
|
||||||
t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_GetRepositorySubscription_true(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `{"subscribed":true}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.GetRepositorySubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Subscription{Subscribed: Bool(true)}
|
|
||||||
if !reflect.DeepEqual(sub, want) {
|
|
||||||
t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_GetRepositorySubscription_false(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
})
|
|
||||||
|
|
||||||
sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.GetRepositorySubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var want *Subscription
|
|
||||||
if !reflect.DeepEqual(sub, want) {
|
|
||||||
t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_GetRepositorySubscription_error(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r")
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected HTTP 400 response")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_SetRepositorySubscription(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &Subscription{Subscribed: Bool(true)}
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(Subscription)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(w, `{"ignored":true}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
sub, _, err := client.Activity.SetRepositorySubscription(context.Background(), "o", "r", input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.SetRepositorySubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Subscription{Ignored: Bool(true)}
|
|
||||||
if !reflect.DeepEqual(sub, want) {
|
|
||||||
t.Errorf("Activity.SetRepositorySubscription returned %+v, want %+v", sub, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActivityService_DeleteRepositorySubscription(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Activity.DeleteRepositorySubscription(context.Background(), "o", "r")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Activity.DeleteRepositorySubscription returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
// Copyright 2016 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AdminService handles communication with the admin related methods of the
|
|
||||||
// GitHub API. These API routes are normally only accessible for GitHub
|
|
||||||
// Enterprise installations.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/enterprise/
|
|
||||||
type AdminService service
|
|
||||||
|
|
||||||
// TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group.
|
|
||||||
type TeamLDAPMapping struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
LDAPDN *string `json:"ldap_dn,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
Slug *string `json:"slug,omitempty"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
Privacy *string `json:"privacy,omitempty"`
|
|
||||||
Permission *string `json:"permission,omitempty"`
|
|
||||||
|
|
||||||
MembersURL *string `json:"members_url,omitempty"`
|
|
||||||
RepositoriesURL *string `json:"repositories_url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m TeamLDAPMapping) String() string {
|
|
||||||
return Stringify(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserLDAPMapping represents the mapping between a GitHub user and an LDAP user.
|
|
||||||
type UserLDAPMapping struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
LDAPDN *string `json:"ldap_dn,omitempty"`
|
|
||||||
Login *string `json:"login,omitempty"`
|
|
||||||
AvatarURL *string `json:"avatar_url,omitempty"`
|
|
||||||
GravatarID *string `json:"gravatar_id,omitempty"`
|
|
||||||
Type *string `json:"type,omitempty"`
|
|
||||||
SiteAdmin *bool `json:"site_admin,omitempty"`
|
|
||||||
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
EventsURL *string `json:"events_url,omitempty"`
|
|
||||||
FollowingURL *string `json:"following_url,omitempty"`
|
|
||||||
FollowersURL *string `json:"followers_url,omitempty"`
|
|
||||||
GistsURL *string `json:"gists_url,omitempty"`
|
|
||||||
OrganizationsURL *string `json:"organizations_url,omitempty"`
|
|
||||||
ReceivedEventsURL *string `json:"received_events_url,omitempty"`
|
|
||||||
ReposURL *string `json:"repos_url,omitempty"`
|
|
||||||
StarredURL *string `json:"starred_url,omitempty"`
|
|
||||||
SubscriptionsURL *string `json:"subscriptions_url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m UserLDAPMapping) String() string {
|
|
||||||
return Stringify(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user
|
|
||||||
func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, mapping *UserLDAPMapping) (*UserLDAPMapping, *Response, error) {
|
|
||||||
u := fmt.Sprintf("admin/ldap/users/%v/mapping", user)
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, mapping)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := new(UserLDAPMapping)
|
|
||||||
resp, err := s.client.Do(ctx, req, m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team
|
|
||||||
func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) {
|
|
||||||
u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team)
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, mapping)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := new(TeamLDAPMapping)
|
|
||||||
resp, err := s.client.Do(ctx, req, m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, resp, nil
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
// Copyright 2016 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAdminService_UpdateUserLDAPMapping(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &UserLDAPMapping{
|
|
||||||
LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/admin/ldap/users/u/mapping", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(UserLDAPMapping)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PATCH")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, `{"id":1,"ldap_dn":"uid=asdf,ou=users,dc=github,dc=com"}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
mapping, _, err := client.Admin.UpdateUserLDAPMapping(context.Background(), "u", input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Admin.UpdateUserLDAPMapping returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &UserLDAPMapping{
|
|
||||||
ID: Int(1),
|
|
||||||
LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(mapping, want) {
|
|
||||||
t.Errorf("Admin.UpdateUserLDAPMapping returned %+v, want %+v", mapping, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdminService_UpdateTeamLDAPMapping(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &TeamLDAPMapping{
|
|
||||||
LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/admin/ldap/teams/1/mapping", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(TeamLDAPMapping)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PATCH")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, `{"id":1,"ldap_dn":"cn=Enterprise Ops,ou=teams,dc=github,dc=com"}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
mapping, _, err := client.Admin.UpdateTeamLDAPMapping(context.Background(), 1, input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Admin.UpdateTeamLDAPMapping returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &TeamLDAPMapping{
|
|
||||||
ID: Int(1),
|
|
||||||
LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(mapping, want) {
|
|
||||||
t.Errorf("Admin.UpdateTeamLDAPMapping returned %+v, want %+v", mapping, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,430 +0,0 @@
|
||||||
// Copyright 2015 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scope models a GitHub authorization scope.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth/#scopes
|
|
||||||
type Scope string
|
|
||||||
|
|
||||||
// This is the set of scopes for GitHub API V3
|
|
||||||
const (
|
|
||||||
ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact?
|
|
||||||
ScopeUser Scope = "user"
|
|
||||||
ScopeUserEmail Scope = "user:email"
|
|
||||||
ScopeUserFollow Scope = "user:follow"
|
|
||||||
ScopePublicRepo Scope = "public_repo"
|
|
||||||
ScopeRepo Scope = "repo"
|
|
||||||
ScopeRepoDeployment Scope = "repo_deployment"
|
|
||||||
ScopeRepoStatus Scope = "repo:status"
|
|
||||||
ScopeDeleteRepo Scope = "delete_repo"
|
|
||||||
ScopeNotifications Scope = "notifications"
|
|
||||||
ScopeGist Scope = "gist"
|
|
||||||
ScopeReadRepoHook Scope = "read:repo_hook"
|
|
||||||
ScopeWriteRepoHook Scope = "write:repo_hook"
|
|
||||||
ScopeAdminRepoHook Scope = "admin:repo_hook"
|
|
||||||
ScopeAdminOrgHook Scope = "admin:org_hook"
|
|
||||||
ScopeReadOrg Scope = "read:org"
|
|
||||||
ScopeWriteOrg Scope = "write:org"
|
|
||||||
ScopeAdminOrg Scope = "admin:org"
|
|
||||||
ScopeReadPublicKey Scope = "read:public_key"
|
|
||||||
ScopeWritePublicKey Scope = "write:public_key"
|
|
||||||
ScopeAdminPublicKey Scope = "admin:public_key"
|
|
||||||
ScopeReadGPGKey Scope = "read:gpg_key"
|
|
||||||
ScopeWriteGPGKey Scope = "write:gpg_key"
|
|
||||||
ScopeAdminGPGKey Scope = "admin:gpg_key"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AuthorizationsService handles communication with the authorization related
|
|
||||||
// methods of the GitHub API.
|
|
||||||
//
|
|
||||||
// This service requires HTTP Basic Authentication; it cannot be accessed using
|
|
||||||
// an OAuth token.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/
|
|
||||||
type AuthorizationsService service
|
|
||||||
|
|
||||||
// Authorization represents an individual GitHub authorization.
|
|
||||||
type Authorization struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
Scopes []Scope `json:"scopes,omitempty"`
|
|
||||||
Token *string `json:"token,omitempty"`
|
|
||||||
TokenLastEight *string `json:"token_last_eight,omitempty"`
|
|
||||||
HashedToken *string `json:"hashed_token,omitempty"`
|
|
||||||
App *AuthorizationApp `json:"app,omitempty"`
|
|
||||||
Note *string `json:"note,omitempty"`
|
|
||||||
NoteURL *string `json:"note_url,omitempty"`
|
|
||||||
UpdateAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
Fingerprint *string `json:"fingerprint,omitempty"`
|
|
||||||
|
|
||||||
// User is only populated by the Check and Reset methods.
|
|
||||||
User *User `json:"user,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Authorization) String() string {
|
|
||||||
return Stringify(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthorizationApp represents an individual GitHub app (in the context of authorization).
|
|
||||||
type AuthorizationApp struct {
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
ClientID *string `json:"client_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AuthorizationApp) String() string {
|
|
||||||
return Stringify(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant represents an OAuth application that has been granted access to an account.
|
|
||||||
type Grant struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
App *AuthorizationApp `json:"app,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
Scopes []string `json:"scopes,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Grant) String() string {
|
|
||||||
return Stringify(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthorizationRequest represents a request to create an authorization.
|
|
||||||
type AuthorizationRequest struct {
|
|
||||||
Scopes []Scope `json:"scopes,omitempty"`
|
|
||||||
Note *string `json:"note,omitempty"`
|
|
||||||
NoteURL *string `json:"note_url,omitempty"`
|
|
||||||
ClientID *string `json:"client_id,omitempty"`
|
|
||||||
ClientSecret *string `json:"client_secret,omitempty"`
|
|
||||||
Fingerprint *string `json:"fingerprint,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AuthorizationRequest) String() string {
|
|
||||||
return Stringify(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthorizationUpdateRequest represents a request to update an authorization.
|
|
||||||
//
|
|
||||||
// Note that for any one update, you must only provide one of the "scopes"
|
|
||||||
// fields. That is, you may provide only one of "Scopes", or "AddScopes", or
|
|
||||||
// "RemoveScopes".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
|
|
||||||
type AuthorizationUpdateRequest struct {
|
|
||||||
Scopes []string `json:"scopes,omitempty"`
|
|
||||||
AddScopes []string `json:"add_scopes,omitempty"`
|
|
||||||
RemoveScopes []string `json:"remove_scopes,omitempty"`
|
|
||||||
Note *string `json:"note,omitempty"`
|
|
||||||
NoteURL *string `json:"note_url,omitempty"`
|
|
||||||
Fingerprint *string `json:"fingerprint,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AuthorizationUpdateRequest) String() string {
|
|
||||||
return Stringify(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// List the authorizations for the authenticated user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations
|
|
||||||
func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*Authorization, *Response, error) {
|
|
||||||
u := "authorizations"
|
|
||||||
u, err := addOptions(u, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var auths []*Authorization
|
|
||||||
resp, err := s.client.Do(ctx, req, &auths)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
return auths, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a single authorization.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization
|
|
||||||
func (s *AuthorizationsService) Get(ctx context.Context, id int) (*Authorization, *Response, error) {
|
|
||||||
u := fmt.Sprintf("authorizations/%d", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new authorization for the specified OAuth application.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization
|
|
||||||
func (s *AuthorizationsService) Create(ctx context.Context, auth *AuthorizationRequest) (*Authorization, *Response, error) {
|
|
||||||
u := "authorizations"
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", u, auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOrCreateForApp creates a new authorization for the specified OAuth
|
|
||||||
// application, only if an authorization for that application doesn’t already
|
|
||||||
// exist for the user.
|
|
||||||
//
|
|
||||||
// If a new token is created, the HTTP status code will be "201 Created", and
|
|
||||||
// the returned Authorization.Token field will be populated. If an existing
|
|
||||||
// token is returned, the status code will be "200 OK" and the
|
|
||||||
// Authorization.Token field will be empty.
|
|
||||||
//
|
|
||||||
// clientID is the OAuth Client ID with which to create the token.
|
|
||||||
//
|
|
||||||
// GitHub API docs:
|
|
||||||
// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app
|
|
||||||
// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint
|
|
||||||
func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) {
|
|
||||||
var u string
|
|
||||||
if auth.Fingerprint == nil || *auth.Fingerprint == "" {
|
|
||||||
u = fmt.Sprintf("authorizations/clients/%v", clientID)
|
|
||||||
} else {
|
|
||||||
u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PUT", u, auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Edit a single authorization.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
|
|
||||||
func (s *AuthorizationsService) Edit(ctx context.Context, id int, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) {
|
|
||||||
u := fmt.Sprintf("authorizations/%d", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("PATCH", u, auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a single authorization.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization
|
|
||||||
func (s *AuthorizationsService) Delete(ctx context.Context, id int) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("authorizations/%d", id)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if an OAuth token is valid for a specific app.
|
|
||||||
//
|
|
||||||
// Note that this operation requires the use of BasicAuth, but where the
|
|
||||||
// username is the OAuth application clientID, and the password is its
|
|
||||||
// clientSecret. Invalid tokens will return a 404 Not Found.
|
|
||||||
//
|
|
||||||
// The returned Authorization.User field will be populated.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization
|
|
||||||
func (s *AuthorizationsService) Check(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) {
|
|
||||||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset is used to reset a valid OAuth token without end user involvement.
|
|
||||||
// Applications must save the "token" property in the response, because changes
|
|
||||||
// take effect immediately.
|
|
||||||
//
|
|
||||||
// Note that this operation requires the use of BasicAuth, but where the
|
|
||||||
// username is the OAuth application clientID, and the password is its
|
|
||||||
// clientSecret. Invalid tokens will return a 404 Not Found.
|
|
||||||
//
|
|
||||||
// The returned Authorization.User field will be populated.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization
|
|
||||||
func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) {
|
|
||||||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("POST", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revoke an authorization for an application.
|
|
||||||
//
|
|
||||||
// Note that this operation requires the use of BasicAuth, but where the
|
|
||||||
// username is the OAuth application clientID, and the password is its
|
|
||||||
// clientSecret. Invalid tokens will return a 404 Not Found.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application
|
|
||||||
func (s *AuthorizationsService) Revoke(ctx context.Context, clientID string, token string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token)
|
|
||||||
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListGrants lists the set of OAuth applications that have been granted
|
|
||||||
// access to a user's account. This will return one entry for each application
|
|
||||||
// that has been granted access to the account, regardless of the number of
|
|
||||||
// tokens an application has generated for the user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants
|
|
||||||
func (s *AuthorizationsService) ListGrants(ctx context.Context) ([]*Grant, *Response, error) {
|
|
||||||
req, err := s.client.NewRequest("GET", "applications/grants", nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
grants := []*Grant{}
|
|
||||||
resp, err := s.client.Do(ctx, req, &grants)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return grants, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGrant gets a single OAuth application grant.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant
|
|
||||||
func (s *AuthorizationsService) GetGrant(ctx context.Context, id int) (*Grant, *Response, error) {
|
|
||||||
u := fmt.Sprintf("applications/grants/%d", id)
|
|
||||||
req, err := s.client.NewRequest("GET", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
grant := new(Grant)
|
|
||||||
resp, err := s.client.Do(ctx, req, grant)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return grant, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteGrant deletes an OAuth application grant. Deleting an application's
|
|
||||||
// grant will also delete all OAuth tokens associated with the application for
|
|
||||||
// the user.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant
|
|
||||||
func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("applications/grants/%d", id)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateImpersonation creates an impersonation OAuth token.
|
|
||||||
//
|
|
||||||
// This requires admin permissions. With the returned Authorization.Token
|
|
||||||
// you can e.g. create or delete a user's public SSH key. NOTE: creating a
|
|
||||||
// new token automatically revokes an existing one.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#create-an-impersonation-oauth-token
|
|
||||||
func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) {
|
|
||||||
u := fmt.Sprintf("admin/users/%v/authorizations", username)
|
|
||||||
req, err := s.client.NewRequest("POST", u, authReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
a := new(Authorization)
|
|
||||||
resp, err := s.client.Do(ctx, req, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, resp, err
|
|
||||||
}
|
|
||||||
return a, resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteImpersonation deletes an impersonation OAuth token.
|
|
||||||
//
|
|
||||||
// NOTE: there can be only one at a time.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#delete-an-impersonation-oauth-token
|
|
||||||
func (s *AuthorizationsService) DeleteImpersonation(ctx context.Context, username string) (*Response, error) {
|
|
||||||
u := fmt.Sprintf("admin/users/%v/authorizations", username)
|
|
||||||
req, err := s.client.NewRequest("DELETE", u, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.Do(ctx, req, nil)
|
|
||||||
}
|
|
|
@ -1,341 +0,0 @@
|
||||||
// Copyright 2015 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAuthorizationsService_List(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
testFormValues(t, r, values{"page": "1", "per_page": "2"})
|
|
||||||
fmt.Fprint(w, `[{"id":1}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
opt := &ListOptions{Page: 1, PerPage: 2}
|
|
||||||
got, _, err := client.Authorizations.List(context.Background(), opt)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.List returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Authorization{{ID: Int(1)}}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Get(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `{"id":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.Get(context.Background(), 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Get returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Create(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &AuthorizationRequest{
|
|
||||||
Note: String("test"),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(AuthorizationRequest)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "POST")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(w, `{"ID":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.Create(context.Background(), input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Create returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorization.Create returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &AuthorizationRequest{
|
|
||||||
Note: String("test"),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(AuthorizationRequest)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(w, `{"ID":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.GetOrCreateForApp(context.Background(), "id", input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &AuthorizationRequest{
|
|
||||||
Note: String("test"),
|
|
||||||
Fingerprint: String("fp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(AuthorizationRequest)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PUT")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(w, `{"ID":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.GetOrCreateForApp(context.Background(), "id", input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Edit(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
input := &AuthorizationUpdateRequest{
|
|
||||||
Note: String("test"),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := new(AuthorizationUpdateRequest)
|
|
||||||
json.NewDecoder(r.Body).Decode(v)
|
|
||||||
|
|
||||||
testMethod(t, r, "PATCH")
|
|
||||||
if !reflect.DeepEqual(v, input) {
|
|
||||||
t.Errorf("Request body = %+v, want %+v", v, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(w, `{"ID":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.Edit(context.Background(), 1, input)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Edit returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorization.Update returned %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Delete(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Authorizations.Delete(context.Background(), 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Delete returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Check(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `{"id":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.Check(context.Background(), "id", "t")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Check returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Reset(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "POST")
|
|
||||||
fmt.Fprint(w, `{"ID":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.Reset(context.Background(), "id", "t")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Reset returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_Revoke(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Authorizations.Revoke(context.Background(), "id", "t")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.Revoke returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListGrants(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `[{"id": 1}]`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.ListGrants(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []*Grant{{ID: Int(1)}}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetGrant(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "GET")
|
|
||||||
fmt.Fprint(w, `{"id": 1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
got, _, err := client.Authorizations.GetGrant(context.Background(), 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Grant{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteGrant(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Authorizations.DeleteGrant(context.Background(), 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_CreateImpersonation(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "POST")
|
|
||||||
fmt.Fprint(w, `{"id":1}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
req := &AuthorizationRequest{Scopes: []Scope{ScopePublicRepo}}
|
|
||||||
got, _, err := client.Authorizations.CreateImpersonation(context.Background(), "u", req)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.CreateImpersonation returned error: %+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &Authorization{ID: Int(1)}
|
|
||||||
if !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("Authorizations.CreateImpersonation returned %+v, want %+v", *got.ID, *want.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthorizationsService_DeleteImpersonation(t *testing.T) {
|
|
||||||
setup()
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
testMethod(t, r, "DELETE")
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := client.Authorizations.DeleteImpersonation(context.Background(), "u")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Authorizations.DeleteImpersonation returned error: %+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
172
vendor/src/github.com/google/go-github/github/doc.go
vendored
172
vendor/src/github.com/google/go-github/github/doc.go
vendored
|
@ -1,172 +0,0 @@
|
||||||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package github provides a client for using the GitHub API.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
import "github.com/google/go-github/github"
|
|
||||||
|
|
||||||
Construct a new GitHub client, then use the various services on the client to
|
|
||||||
access different parts of the GitHub API. For example:
|
|
||||||
|
|
||||||
client := github.NewClient(nil)
|
|
||||||
|
|
||||||
// list all organizations for user "willnorris"
|
|
||||||
orgs, _, err := client.Organizations.List(ctx, "willnorris", nil)
|
|
||||||
|
|
||||||
Some API methods have optional parameters that can be passed. For example:
|
|
||||||
|
|
||||||
client := github.NewClient(nil)
|
|
||||||
|
|
||||||
// list public repositories for org "github"
|
|
||||||
opt := &github.RepositoryListByOrgOptions{Type: "public"}
|
|
||||||
repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt)
|
|
||||||
|
|
||||||
The services of a client divide the API into logical chunks and correspond to
|
|
||||||
the structure of the GitHub API documentation at
|
|
||||||
https://developer.github.com/v3/.
|
|
||||||
|
|
||||||
Authentication
|
|
||||||
|
|
||||||
The go-github library does not directly handle authentication. Instead, when
|
|
||||||
creating a new client, pass an http.Client that can handle authentication for
|
|
||||||
you. The easiest and recommended way to do this is using the golang.org/x/oauth2
|
|
||||||
library, but you can always use any other library that provides an http.Client.
|
|
||||||
If you have an OAuth2 access token (for example, a personal API token), you can
|
|
||||||
use it with the oauth2 library using:
|
|
||||||
|
|
||||||
import "golang.org/x/oauth2"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx := context.Background()
|
|
||||||
ts := oauth2.StaticTokenSource(
|
|
||||||
&oauth2.Token{AccessToken: "... your access token ..."},
|
|
||||||
)
|
|
||||||
tc := oauth2.NewClient(ctx, ts)
|
|
||||||
|
|
||||||
client := github.NewClient(tc)
|
|
||||||
|
|
||||||
// list all repositories for the authenticated user
|
|
||||||
repos, _, err := client.Repositories.List(ctx, "", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
Note that when using an authenticated Client, all calls made by the client will
|
|
||||||
include the specified OAuth token. Therefore, authenticated clients should
|
|
||||||
almost never be shared between different users.
|
|
||||||
|
|
||||||
See the oauth2 docs for complete instructions on using that library.
|
|
||||||
|
|
||||||
For API methods that require HTTP Basic Authentication, use the
|
|
||||||
BasicAuthTransport.
|
|
||||||
|
|
||||||
Rate Limiting
|
|
||||||
|
|
||||||
GitHub imposes a rate limit on all API clients. Unauthenticated clients are
|
|
||||||
limited to 60 requests per hour, while authenticated clients can make up to
|
|
||||||
5,000 requests per hour. To receive the higher rate limit when making calls
|
|
||||||
that are not issued on behalf of a user, use the
|
|
||||||
UnauthenticatedRateLimitedTransport.
|
|
||||||
|
|
||||||
The Rate method on a client returns the rate limit information based on the most
|
|
||||||
recent API call. This is updated on every call, but may be out of date if it's
|
|
||||||
been some time since the last API call and other clients have made subsequent
|
|
||||||
requests since then. You can always call RateLimits() directly to get the most
|
|
||||||
up-to-date rate limit data for the client.
|
|
||||||
|
|
||||||
To detect an API rate limit error, you can check if its type is *github.RateLimitError:
|
|
||||||
|
|
||||||
repos, _, err := client.Repositories.List(ctx, "", nil)
|
|
||||||
if _, ok := err.(*github.RateLimitError); ok {
|
|
||||||
log.Println("hit rate limit")
|
|
||||||
}
|
|
||||||
|
|
||||||
Learn more about GitHub rate limiting at
|
|
||||||
https://developer.github.com/v3/#rate-limiting.
|
|
||||||
|
|
||||||
Accepted Status
|
|
||||||
|
|
||||||
Some endpoints may return a 202 Accepted status code, meaning that the
|
|
||||||
information required is not yet ready and was scheduled to be gathered on
|
|
||||||
the GitHub side. Methods known to behave like this are documented specifying
|
|
||||||
this behavior.
|
|
||||||
|
|
||||||
To detect this condition of error, you can check if its type is
|
|
||||||
*github.AcceptedError:
|
|
||||||
|
|
||||||
stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo)
|
|
||||||
if _, ok := err.(*github.AcceptedError); ok {
|
|
||||||
log.Println("scheduled on GitHub side")
|
|
||||||
}
|
|
||||||
|
|
||||||
Conditional Requests
|
|
||||||
|
|
||||||
The GitHub API has good support for conditional requests which will help
|
|
||||||
prevent you from burning through your rate limit, as well as help speed up your
|
|
||||||
application. go-github does not handle conditional requests directly, but is
|
|
||||||
instead designed to work with a caching http.Transport. We recommend using
|
|
||||||
https://github.com/gregjones/httpcache for that.
|
|
||||||
|
|
||||||
Learn more about GitHub conditional requests at
|
|
||||||
https://developer.github.com/v3/#conditional-requests.
|
|
||||||
|
|
||||||
Creating and Updating Resources
|
|
||||||
|
|
||||||
All structs for GitHub resources use pointer values for all non-repeated fields.
|
|
||||||
This allows distinguishing between unset fields and those set to a zero-value.
|
|
||||||
Helper functions have been provided to easily create these pointers for string,
|
|
||||||
bool, and int values. For example:
|
|
||||||
|
|
||||||
// create a new private repository named "foo"
|
|
||||||
repo := &github.Repository{
|
|
||||||
Name: github.String("foo"),
|
|
||||||
Private: github.Bool(true),
|
|
||||||
}
|
|
||||||
client.Repositories.Create(ctx, "", repo)
|
|
||||||
|
|
||||||
Users who have worked with protocol buffers should find this pattern familiar.
|
|
||||||
|
|
||||||
Pagination
|
|
||||||
|
|
||||||
All requests for resource collections (repos, pull requests, issues, etc.)
|
|
||||||
support pagination. Pagination options are described in the
|
|
||||||
github.ListOptions struct and passed to the list methods directly or as an
|
|
||||||
embedded type of a more specific list options struct (for example
|
|
||||||
github.PullRequestListOptions). Pages information is available via the
|
|
||||||
github.Response struct.
|
|
||||||
|
|
||||||
client := github.NewClient(nil)
|
|
||||||
|
|
||||||
opt := &github.RepositoryListByOrgOptions{
|
|
||||||
ListOptions: github.ListOptions{PerPage: 10},
|
|
||||||
}
|
|
||||||
// get all pages of results
|
|
||||||
var allRepos []*github.Repository
|
|
||||||
for {
|
|
||||||
repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
allRepos = append(allRepos, repos...)
|
|
||||||
if resp.NextPage == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
opt.ListOptions.Page = resp.NextPage
|
|
||||||
}
|
|
||||||
|
|
||||||
Google App Engine
|
|
||||||
|
|
||||||
Go on App Engine Classic (which as of this writing uses Go 1.6) can not use
|
|
||||||
the "context" import and still relies on "golang.org/x/net/context".
|
|
||||||
As a result, if you wish to continue to use "go-github" on App Engine Classic,
|
|
||||||
you will need to rewrite all the "context" imports using the following command:
|
|
||||||
|
|
||||||
gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go
|
|
||||||
|
|
||||||
See "with_appengine.go" for more details.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package github
|
|
|
@ -1,672 +0,0 @@
|
||||||
// Copyright 2016 The go-github AUTHORS. All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// These event types are shared between the Events API and used as Webhook payloads.
|
|
||||||
|
|
||||||
package github
|
|
||||||
|
|
||||||
// CommitCommentEvent is triggered when a commit comment is created.
|
|
||||||
// The Webhook event name is "commit_comment".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent
|
|
||||||
type CommitCommentEvent struct {
|
|
||||||
Comment *RepositoryComment `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateEvent represents a created repository, branch, or tag.
|
|
||||||
// The Webhook event name is "create".
|
|
||||||
//
|
|
||||||
// Note: webhooks will not receive this event for created repositories.
|
|
||||||
// Additionally, webhooks will not receive this event for tags if more
|
|
||||||
// than three tags are pushed at once.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#createevent
|
|
||||||
type CreateEvent struct {
|
|
||||||
Ref *string `json:"ref,omitempty"`
|
|
||||||
// RefType is the object that was created. Possible values are: "repository", "branch", "tag".
|
|
||||||
RefType *string `json:"ref_type,omitempty"`
|
|
||||||
MasterBranch *string `json:"master_branch,omitempty"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
PusherType *string `json:"pusher_type,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteEvent represents a deleted branch or tag.
|
|
||||||
// The Webhook event name is "delete".
|
|
||||||
//
|
|
||||||
// Note: webhooks will not receive this event for tags if more than three tags
|
|
||||||
// are deleted at once.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deleteevent
|
|
||||||
type DeleteEvent struct {
|
|
||||||
Ref *string `json:"ref,omitempty"`
|
|
||||||
// RefType is the object that was deleted. Possible values are: "branch", "tag".
|
|
||||||
RefType *string `json:"ref_type,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
PusherType *string `json:"pusher_type,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeploymentEvent represents a deployment.
|
|
||||||
// The Webhook event name is "deployment".
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines, they are only used to trigger hooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentevent
|
|
||||||
type DeploymentEvent struct {
|
|
||||||
Deployment *Deployment `json:"deployment,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeploymentStatusEvent represents a deployment status.
|
|
||||||
// The Webhook event name is "deployment_status".
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines, they are only used to trigger hooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent
|
|
||||||
type DeploymentStatusEvent struct {
|
|
||||||
Deployment *Deployment `json:"deployment,omitempty"`
|
|
||||||
DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForkEvent is triggered when a user forks a repository.
|
|
||||||
// The Webhook event name is "fork".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#forkevent
|
|
||||||
type ForkEvent struct {
|
|
||||||
// Forkee is the created repository.
|
|
||||||
Forkee *Repository `json:"forkee,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page represents a single Wiki page.
|
|
||||||
type Page struct {
|
|
||||||
PageName *string `json:"page_name,omitempty"`
|
|
||||||
Title *string `json:"title,omitempty"`
|
|
||||||
Summary *string `json:"summary,omitempty"`
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
SHA *string `json:"sha,omitempty"`
|
|
||||||
HTMLURL *string `json:"html_url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GollumEvent is triggered when a Wiki page is created or updated.
|
|
||||||
// The Webhook event name is "gollum".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#gollumevent
|
|
||||||
type GollumEvent struct {
|
|
||||||
Pages []*Page `json:"pages,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EditChange represents the changes when an issue, pull request, or comment has
|
|
||||||
// been edited.
|
|
||||||
type EditChange struct {
|
|
||||||
Title *struct {
|
|
||||||
From *string `json:"from,omitempty"`
|
|
||||||
} `json:"title,omitempty"`
|
|
||||||
Body *struct {
|
|
||||||
From *string `json:"from,omitempty"`
|
|
||||||
} `json:"body,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectChange represents the changes when a project has been edited.
|
|
||||||
type ProjectChange struct {
|
|
||||||
Name *struct {
|
|
||||||
From *string `json:"from,omitempty"`
|
|
||||||
} `json:"name,omitempty"`
|
|
||||||
Body *struct {
|
|
||||||
From *string `json:"from,omitempty"`
|
|
||||||
} `json:"body,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectCardChange represents the changes when a project card has been edited.
|
|
||||||
type ProjectCardChange struct {
|
|
||||||
Note *struct {
|
|
||||||
From *string `json:"from,omitempty"`
|
|
||||||
} `json:"note,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectColumnChange represents the changes when a project column has been edited.
|
|
||||||
type ProjectColumnChange struct {
|
|
||||||
Name *struct {
|
|
||||||
From *string `json:"from,omitempty"`
|
|
||||||
} `json:"name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntegrationInstallationEvent is triggered when an integration is created or deleted.
|
|
||||||
// The Webhook event name is "integration_installation".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/early-access/integrations/webhooks/#integrationinstallationevent
|
|
||||||
type IntegrationInstallationEvent struct {
|
|
||||||
// The action that was performed. Possible values for an "integration_installation"
|
|
||||||
// event are: "created", "deleted".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntegrationInstallationRepositoriesEvent is triggered when an integration repository
|
|
||||||
// is added or removed. The Webhook event name is "integration_installation_repositories".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/early-access/integrations/webhooks/#integrationinstallationrepositoriesevent
|
|
||||||
type IntegrationInstallationRepositoriesEvent struct {
|
|
||||||
// The action that was performed. Possible values for an "integration_installation_repositories"
|
|
||||||
// event are: "added", "removed".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
RepositoriesAdded []*Repository `json:"repositories_added,omitempty"`
|
|
||||||
RepositoriesRemoved []*Repository `json:"repositories_removed,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssueCommentEvent is triggered when an issue comment is created on an issue
|
|
||||||
// or pull request.
|
|
||||||
// The Webhook event name is "issue_comment".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent
|
|
||||||
type IssueCommentEvent struct {
|
|
||||||
// Action is the action that was performed on the comment.
|
|
||||||
// Possible values are: "created", "edited", "deleted".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Issue *Issue `json:"issue,omitempty"`
|
|
||||||
Comment *IssueComment `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Changes *EditChange `json:"changes,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssuesEvent is triggered when an issue is assigned, unassigned, labeled,
|
|
||||||
// unlabeled, opened, closed, or reopened.
|
|
||||||
// The Webhook event name is "issues".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuesevent
|
|
||||||
type IssuesEvent struct {
|
|
||||||
// Action is the action that was performed. Possible values are: "assigned",
|
|
||||||
// "unassigned", "labeled", "unlabeled", "opened", "closed", "reopened", "edited".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Issue *Issue `json:"issue,omitempty"`
|
|
||||||
Assignee *User `json:"assignee,omitempty"`
|
|
||||||
Label *Label `json:"label,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Changes *EditChange `json:"changes,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// LabelEvent is triggered when a repository's label is created, edited, or deleted.
|
|
||||||
// The Webhook event name is "label"
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#labelevent
|
|
||||||
type LabelEvent struct {
|
|
||||||
// Action is the action that was performed. Possible values are:
|
|
||||||
// "created", "edited", "deleted"
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Label *Label `json:"label,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Changes *EditChange `json:"changes,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MemberEvent is triggered when a user is added as a collaborator to a repository.
|
|
||||||
// The Webhook event name is "member".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#memberevent
|
|
||||||
type MemberEvent struct {
|
|
||||||
// Action is the action that was performed. Possible value is: "added".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Member *User `json:"member,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MembershipEvent is triggered when a user is added or removed from a team.
|
|
||||||
// The Webhook event name is "membership".
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines, they are only used to
|
|
||||||
// trigger organization webhooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#membershipevent
|
|
||||||
type MembershipEvent struct {
|
|
||||||
// Action is the action that was performed. Possible values are: "added", "removed".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
// Scope is the scope of the membership. Possible value is: "team".
|
|
||||||
Scope *string `json:"scope,omitempty"`
|
|
||||||
Member *User `json:"member,omitempty"`
|
|
||||||
Team *Team `json:"team,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted.
|
|
||||||
// The Webhook event name is "milestone".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#milestoneevent
|
|
||||||
type MilestoneEvent struct {
|
|
||||||
// Action is the action that was performed. Possible values are:
|
|
||||||
// "created", "closed", "opened", "edited", "deleted"
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Milestone *Milestone `json:"milestone,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Changes *EditChange `json:"changes,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// OrganizationEvent is triggered when a user is added, removed, or invited to an organization.
|
|
||||||
// Events of this type are not visible in timelines. These events are only used to trigger organization hooks.
|
|
||||||
// Webhook event name is "organization".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#organizationevent
|
|
||||||
type OrganizationEvent struct {
|
|
||||||
// Action is the action that was performed.
|
|
||||||
// Can be one of "member_added", "member_removed", or "member_invited".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
|
|
||||||
// Invitaion is the invitation for the user or email if the action is "member_invited".
|
|
||||||
Invitation *Invitation `json:"invitation,omitempty"`
|
|
||||||
|
|
||||||
// Membership is the membership between the user and the organization.
|
|
||||||
// Not present when the action is "member_invited".
|
|
||||||
Membership *Membership `json:"membership,omitempty"`
|
|
||||||
|
|
||||||
Organization *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether
|
|
||||||
// successful or not.
|
|
||||||
// The Webhook event name is "page_build".
|
|
||||||
//
|
|
||||||
// This event is triggered on push to a GitHub Pages enabled branch (gh-pages
|
|
||||||
// for project pages, master for user and organization pages).
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines, they are only used to trigger hooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent
|
|
||||||
type PageBuildEvent struct {
|
|
||||||
Build *PagesBuild `json:"build,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PingEvent is triggered when a Webhook is added to GitHub.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/webhooks/#ping-event
|
|
||||||
type PingEvent struct {
|
|
||||||
// Random string of GitHub zen.
|
|
||||||
Zen *string `json:"zen,omitempty"`
|
|
||||||
// The ID of the webhook that triggered the ping.
|
|
||||||
HookID *int `json:"hook_id,omitempty"`
|
|
||||||
// The webhook configuration.
|
|
||||||
Hook *Hook `json:"hook,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectEvent is triggered when project is created, modified or deleted.
|
|
||||||
// The webhook event name is "project".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectevent
|
|
||||||
type ProjectEvent struct {
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Changes *ProjectChange `json:"changes,omitempty"`
|
|
||||||
Project *Project `json:"project,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectCardEvent is triggered when a project card is created, updated, moved, converted to an issue, or deleted.
|
|
||||||
// The webhook event name is "project_card".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcardevent
|
|
||||||
type ProjectCardEvent struct {
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Changes *ProjectCardChange `json:"changes,omitempty"`
|
|
||||||
AfterID *int `json:"after_id,omitempty"`
|
|
||||||
ProjectCard *ProjectCard `json:"project_card,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectColumnEvent is triggered when a project column is created, updated, moved, or deleted.
|
|
||||||
// The webhook event name is "project_column".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcolumnevent
|
|
||||||
type ProjectColumnEvent struct {
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Changes *ProjectColumnChange `json:"changes,omitempty"`
|
|
||||||
AfterID *int `json:"after_id,omitempty"`
|
|
||||||
ProjectColumn *ProjectColumn `json:"project_column,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicEvent is triggered when a private repository is open sourced.
|
|
||||||
// According to GitHub: "Without a doubt: the best GitHub event."
|
|
||||||
// The Webhook event name is "public".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#publicevent
|
|
||||||
type PublicEvent struct {
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PullRequestEvent is triggered when a pull request is assigned, unassigned,
|
|
||||||
// labeled, unlabeled, opened, closed, reopened, or synchronized.
|
|
||||||
// The Webhook event name is "pull_request".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent
|
|
||||||
type PullRequestEvent struct {
|
|
||||||
// Action is the action that was performed. Possible values are: "assigned",
|
|
||||||
// "unassigned", "labeled", "unlabeled", "opened", "closed", or "reopened",
|
|
||||||
// "synchronize", "edited". If the action is "closed" and the merged key is false,
|
|
||||||
// the pull request was closed with unmerged commits. If the action is "closed"
|
|
||||||
// and the merged key is true, the pull request was merged.
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Number *int `json:"number,omitempty"`
|
|
||||||
PullRequest *PullRequest `json:"pull_request,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Changes *EditChange `json:"changes,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PullRequestReviewEvent is triggered when a review is submitted on a pull
|
|
||||||
// request.
|
|
||||||
// The Webhook event name is "pull_request_review".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent
|
|
||||||
type PullRequestReviewEvent struct {
|
|
||||||
// Action is always "submitted".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Review *PullRequestReview `json:"review,omitempty"`
|
|
||||||
PullRequest *PullRequest `json:"pull_request,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
|
|
||||||
// The following field is only present when the webhook is triggered on
|
|
||||||
// a repository belonging to an organization.
|
|
||||||
Organization *Organization `json:"organization,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PullRequestReviewCommentEvent is triggered when a comment is created on a
|
|
||||||
// portion of the unified diff of a pull request.
|
|
||||||
// The Webhook event name is "pull_request_review_comment".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
|
|
||||||
type PullRequestReviewCommentEvent struct {
|
|
||||||
// Action is the action that was performed on the comment.
|
|
||||||
// Possible values are: "created", "edited", "deleted".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
PullRequest *PullRequest `json:"pull_request,omitempty"`
|
|
||||||
Comment *PullRequestComment `json:"comment,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Changes *EditChange `json:"changes,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushEvent represents a git push to a GitHub repository.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent
|
|
||||||
type PushEvent struct {
|
|
||||||
PushID *int `json:"push_id,omitempty"`
|
|
||||||
Head *string `json:"head,omitempty"`
|
|
||||||
Ref *string `json:"ref,omitempty"`
|
|
||||||
Size *int `json:"size,omitempty"`
|
|
||||||
Commits []PushEventCommit `json:"commits,omitempty"`
|
|
||||||
Before *string `json:"before,omitempty"`
|
|
||||||
DistinctSize *int `json:"distinct_size,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
After *string `json:"after,omitempty"`
|
|
||||||
Created *bool `json:"created,omitempty"`
|
|
||||||
Deleted *bool `json:"deleted,omitempty"`
|
|
||||||
Forced *bool `json:"forced,omitempty"`
|
|
||||||
BaseRef *string `json:"base_ref,omitempty"`
|
|
||||||
Compare *string `json:"compare,omitempty"`
|
|
||||||
Repo *PushEventRepository `json:"repository,omitempty"`
|
|
||||||
HeadCommit *PushEventCommit `json:"head_commit,omitempty"`
|
|
||||||
Pusher *User `json:"pusher,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p PushEvent) String() string {
|
|
||||||
return Stringify(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushEventCommit represents a git commit in a GitHub PushEvent.
|
|
||||||
type PushEventCommit struct {
|
|
||||||
Message *string `json:"message,omitempty"`
|
|
||||||
Author *CommitAuthor `json:"author,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
Distinct *bool `json:"distinct,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Events API.
|
|
||||||
SHA *string `json:"sha,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
ID *string `json:"id,omitempty"`
|
|
||||||
TreeID *string `json:"tree_id,omitempty"`
|
|
||||||
Timestamp *Timestamp `json:"timestamp,omitempty"`
|
|
||||||
Committer *CommitAuthor `json:"committer,omitempty"`
|
|
||||||
Added []string `json:"added,omitempty"`
|
|
||||||
Removed []string `json:"removed,omitempty"`
|
|
||||||
Modified []string `json:"modified,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p PushEventCommit) String() string {
|
|
||||||
return Stringify(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushEventRepository represents the repo object in a PushEvent payload.
|
|
||||||
type PushEventRepository struct {
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
FullName *string `json:"full_name,omitempty"`
|
|
||||||
Owner *PushEventRepoOwner `json:"owner,omitempty"`
|
|
||||||
Private *bool `json:"private,omitempty"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
Fork *bool `json:"fork,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
PushedAt *Timestamp `json:"pushed_at,omitempty"`
|
|
||||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
Homepage *string `json:"homepage,omitempty"`
|
|
||||||
Size *int `json:"size,omitempty"`
|
|
||||||
StargazersCount *int `json:"stargazers_count,omitempty"`
|
|
||||||
WatchersCount *int `json:"watchers_count,omitempty"`
|
|
||||||
Language *string `json:"language,omitempty"`
|
|
||||||
HasIssues *bool `json:"has_issues,omitempty"`
|
|
||||||
HasDownloads *bool `json:"has_downloads,omitempty"`
|
|
||||||
HasWiki *bool `json:"has_wiki,omitempty"`
|
|
||||||
HasPages *bool `json:"has_pages,omitempty"`
|
|
||||||
ForksCount *int `json:"forks_count,omitempty"`
|
|
||||||
OpenIssuesCount *int `json:"open_issues_count,omitempty"`
|
|
||||||
DefaultBranch *string `json:"default_branch,omitempty"`
|
|
||||||
MasterBranch *string `json:"master_branch,omitempty"`
|
|
||||||
Organization *string `json:"organization,omitempty"`
|
|
||||||
URL *string `json:"url,omitempty"`
|
|
||||||
ArchiveURL *string `json:"archive_url,omitempty"`
|
|
||||||
HTMLURL *string `json:"html_url,omitempty"`
|
|
||||||
StatusesURL *string `json:"statuses_url,omitempty"`
|
|
||||||
GitURL *string `json:"git_url,omitempty"`
|
|
||||||
SSHURL *string `json:"ssh_url,omitempty"`
|
|
||||||
CloneURL *string `json:"clone_url,omitempty"`
|
|
||||||
SVNURL *string `json:"svn_url,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushEventRepoOwner is a basic representation of user/org in a PushEvent payload.
|
|
||||||
type PushEventRepoOwner struct {
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
Email *string `json:"email,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseEvent is triggered when a release is published.
|
|
||||||
// The Webhook event name is "release".
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#releaseevent
|
|
||||||
type ReleaseEvent struct {
|
|
||||||
// Action is the action that was performed. Possible value is: "published".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Release *RepositoryRelease `json:"release,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RepositoryEvent is triggered when a repository is created.
|
|
||||||
// The Webhook event name is "repository".
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines, they are only used to
|
|
||||||
// trigger organization webhooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryevent
|
|
||||||
type RepositoryEvent struct {
|
|
||||||
// Action is the action that was performed. Possible values are: "created", "deleted",
|
|
||||||
// "publicized", "privatized".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatusEvent is triggered when the status of a Git commit changes.
|
|
||||||
// The Webhook event name is "status".
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines, they are only used to
|
|
||||||
// trigger hooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#statusevent
|
|
||||||
type StatusEvent struct {
|
|
||||||
SHA *string `json:"sha,omitempty"`
|
|
||||||
// State is the new state. Possible values are: "pending", "success", "failure", "error".
|
|
||||||
State *string `json:"state,omitempty"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
TargetURL *string `json:"target_url,omitempty"`
|
|
||||||
Branches []*Branch `json:"branches,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
ID *int `json:"id,omitempty"`
|
|
||||||
Name *string `json:"name,omitempty"`
|
|
||||||
Context *string `json:"context,omitempty"`
|
|
||||||
Commit *RepositoryCommit `json:"commit,omitempty"`
|
|
||||||
CreatedAt *Timestamp `json:"created_at,omitempty"`
|
|
||||||
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TeamAddEvent is triggered when a repository is added to a team.
|
|
||||||
// The Webhook event name is "team_add".
|
|
||||||
//
|
|
||||||
// Events of this type are not visible in timelines. These events are only used
|
|
||||||
// to trigger hooks.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamaddevent
|
|
||||||
type TeamAddEvent struct {
|
|
||||||
Team *Team `json:"team,omitempty"`
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Org *Organization `json:"organization,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchEvent is related to starring a repository, not watching. See this API
|
|
||||||
// blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/
|
|
||||||
//
|
|
||||||
// The event’s actor is the user who starred a repository, and the event’s
|
|
||||||
// repository is the repository that was starred.
|
|
||||||
//
|
|
||||||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#watchevent
|
|
||||||
type WatchEvent struct {
|
|
||||||
// Action is the action that was performed. Possible value is: "started".
|
|
||||||
Action *string `json:"action,omitempty"`
|
|
||||||
|
|
||||||
// The following fields are only populated by Webhook events.
|
|
||||||
Repo *Repository `json:"repository,omitempty"`
|
|
||||||
Sender *User `json:"sender,omitempty"`
|
|
||||||
Installation *Installation `json:"installation,omitempty"`
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue