Compare commits
	
		
			No commits in common. "ff2a9e21b590e8ad77eebc9c416e2c32c6495986" and "d244cbf5898d1f04f9dbddeba376403b16dad03e" have entirely different histories.
		
	
	
		
			ff2a9e21b5
			...
			d244cbf589
		
	
		
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,12 +1,5 @@ | |||||||
| /goserv | /goserv | ||||||
| *.claims.json |  | ||||||
| *.jwk.json |  | ||||||
| *.jws.json |  | ||||||
| *.jwt.txt |  | ||||||
| 
 | 
 | ||||||
| xversion.go |  | ||||||
| 
 |  | ||||||
| *_string.go |  | ||||||
| *_vfsdata.go | *_vfsdata.go | ||||||
| *.env | *.env | ||||||
| .env | .env | ||||||
|  | |||||||
| @ -1,37 +0,0 @@ | |||||||
| # This is an example goreleaser.yaml file with some sane defaults. |  | ||||||
| # Make sure to check the documentation at http://goreleaser.com |  | ||||||
| before: |  | ||||||
|   hooks: |  | ||||||
|     # You may remove this if you don't use go modules. |  | ||||||
|     - go mod download |  | ||||||
|     # you may remove this if you don't need go generate |  | ||||||
|     - go generate ./... |  | ||||||
| builds: |  | ||||||
|   - env: |  | ||||||
|       - CGO_ENABLED=0 |  | ||||||
|     goos: |  | ||||||
|       - linux |  | ||||||
|       - windows |  | ||||||
|       - darwin |  | ||||||
|     goarch: |  | ||||||
|       - amd64 |  | ||||||
|       - arm64 |  | ||||||
|       - arm |  | ||||||
| archives: |  | ||||||
|   - replacements: |  | ||||||
|       darwin: Darwin |  | ||||||
|       linux: Linux |  | ||||||
|       windows: Windows |  | ||||||
|       386: i386 |  | ||||||
|       amd64: x86_64 |  | ||||||
|       arm64: aarch64 |  | ||||||
| checksum: |  | ||||||
|   name_template: 'checksums.txt' |  | ||||||
| snapshot: |  | ||||||
|   name_template: "{{ .Tag }}-next" |  | ||||||
| changelog: |  | ||||||
|   sort: asc |  | ||||||
|   filters: |  | ||||||
|     exclude: |  | ||||||
|       - '^docs:' |  | ||||||
|       - '^test:' |  | ||||||
							
								
								
									
										205
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										205
									
								
								README.md
									
									
									
									
									
								
							| @ -1,207 +1,38 @@ | |||||||
| # [GoServ](https://git.coolaj86.com/coolaj86/goserv) | # goserv | ||||||
| 
 |  | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
| > Boilerplate for how I like to write a backend web service | > Boilerplate for how I like to write a backend web service | ||||||
| 
 | 
 | ||||||
| ## Build | ## Build | ||||||
| 
 | 
 | ||||||
| #### Goreleaser |  | ||||||
| 
 |  | ||||||
| See <https://webinstall.dev/goreleaser> |  | ||||||
| 
 |  | ||||||
| Local-only |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| goreleaser --snapshot --skip-publish --rm-dist |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Publish |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| # Get a token at https://github.com/settings/tokens |  | ||||||
| export GITHUB_TOKEN=xxxxxxx |  | ||||||
| 
 |  | ||||||
| # Remove --snapshot to error on non-clean releases |  | ||||||
| goreleaser --snapshot --rm-dist |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| The platform, publish URL, and token file can be changed in `.goreleaser.yml`: |  | ||||||
| 
 |  | ||||||
| ```yml |  | ||||||
| env_files: |  | ||||||
|   gitea_token: ~/.config/goreleaser/gitea_token |  | ||||||
| gitea_urls: |  | ||||||
|   api: https://try.gitea.io/api/v1/ |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| #### Manually |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| git clone ssh://gitea@git.coolaj86.com:22042/coolaj86/goserv.git ./goserv |  | ||||||
| pushd ./goserv |  | ||||||
| bash ./examples/build.sh |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ```bash | ```bash | ||||||
| export GOFLAGS="-mod=vendor" | export GOFLAGS="-mod=vendor" | ||||||
| go mod tidy | go mod tidy | ||||||
| go mod vendor | go mod vendor | ||||||
| go generate -mod=vendor ./... | go generate -mod=vendor ./... | ||||||
| go build -mod=vendor -o dist/goserv . | go build -mod=vendor . | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| To build for another platform (such as Raspberry Pi) set `GOOS` and GOARCH`: |  | ||||||
| 
 |  | ||||||
| ```bash | ```bash | ||||||
| GOOS=linux GOARCH=arm64 go build -mod=vendor -o dist/goserv-linux-arm64 . | ./goserv run --listen :3000 --serve-path ./overrides | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| #### Run | ## Eamples and Config Templates | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| ./dist/goserv run --listen :3000 --trust-proxy --serve-path ./overrides |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Examples and Config Templates |  | ||||||
| 
 | 
 | ||||||
| The example files are located in `./examples` | The example files are located in `./examples` | ||||||
| 
 | 
 | ||||||
| - Caddyfile (web server config) | -   Caddyfile (web server config) | ||||||
| - .env (environment variables) | -   .env (environment variables) | ||||||
| - build.sh | -   build.sh | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| export BASE_URL=https://example.com |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ### Authentication |  | ||||||
| 
 |  | ||||||
| You can use an OIDC provider or sign your own tokens. |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go install -mod=vendor git.rootprojects.org/root/keypairs/cmd/keypairs |  | ||||||
| 
 |  | ||||||
| # Generate a keypair |  | ||||||
| keypairs gen -o key.jwk.json --pub pub.jwk.json |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Create an Admin token: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| # Sign an Admin token |  | ||||||
| echo '{ "sub": "random_ppid", "email": "me@example.com", "iss": "'"${BASE_URL}"'" }' \ |  | ||||||
|     > admin.claims.json |  | ||||||
| keypairs sign --exp 1h ./key.jwk.json ./admin.claims.json > admin.jwt.txt 2> admin.jws.json |  | ||||||
| 
 |  | ||||||
| # verify the Admin token |  | ||||||
| keypairs verify ./pub.jwk.json ./admin.jwt.txt |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Create a User token: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| # Sign a User token |  | ||||||
| echo '{ "sub": "random_ppid", "email": "me@example.com", "iss": "'"${BASE_URL}"'" }' \ |  | ||||||
|     > user.claims.json |  | ||||||
| keypairs sign --exp 1h ./key.jwk.json ./user.claims.json > user.jwt.txt 2> user.jws.json |  | ||||||
| 
 |  | ||||||
| # verify the User token |  | ||||||
| keypairs verify ./pub.jwk.json ./user.jwt.txt |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| **Impersonation** can be accomplished by appending the token `sub` (aka PPID) to the url as |  | ||||||
| `?user_id=`. |  | ||||||
| 
 |  | ||||||
| ## REST API |  | ||||||
| 
 |  | ||||||
| All routes require authentication, except for those at `/api/public`. |  | ||||||
| 
 |  | ||||||
| ```txt |  | ||||||
| Authentication: Bearer <token> |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Here's the API, in brief: |  | ||||||
| 
 |  | ||||||
| ```txt |  | ||||||
| # Demo Mode Only |  | ||||||
| DELETE /public/reset                                    Drop database and re-initialize |  | ||||||
| 
 |  | ||||||
| # Public |  | ||||||
| GET  /public/ping                                       Health Check |  | ||||||
| POST /public/setup                  <= (none)           Bootstrap |  | ||||||
| 
 |  | ||||||
| # Admin-only |  | ||||||
| GET  /admin/ping                                        (authenticated) Health Check |  | ||||||
| 
 |  | ||||||
| # User |  | ||||||
| GET  /user/ping                                         (authenticated) Health Check |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| When you `GET` anything, it will be wrapped in the `result`. |  | ||||||
| 
 |  | ||||||
| #### Bootstrapping |  | ||||||
| 
 |  | ||||||
| The first user to hit the `/api/setup` endpoint will be the **admin**: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| export TOKEN=$(cat admin.jwt.txt) |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| curl -X POST "${BASE_URL}/api/public/setup" -H "Authorization: Bearer ${TOKEN}" |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Then the setup endpoint will be disabled: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| curl -X POST "${BASE_URL}/api/public/setup" -H "Authorization: Bearer ${TOKEN}" |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## GoDoc |  | ||||||
| 
 |  | ||||||
| If the documentation is not public hosted you can view it with GoDoc: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| godoc --http :6060 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| - <http://localhost:6060/pkg/git.example.com/example/goserv/> |  | ||||||
| - <http://localhost:6060/pkg/git.example.com/example/goserv/internal/api> |  | ||||||
| - <http://localhost:6060/pkg/git.example.com/example/goserv/internal/db> |  | ||||||
| 
 |  | ||||||
| You can see abbreviated documentation with `go`'s built-in `doc`, for example: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go doc git.example.com/example/goserv/internal/api |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| <http://localhost:6060> |  | ||||||
| 
 |  | ||||||
| ## Test |  | ||||||
| 
 |  | ||||||
| Create a test database. It must have `test` in the name. |  | ||||||
| 
 |  | ||||||
| ```sql |  | ||||||
| DROP DATABASE "goserv_test"; CREATE DATABASE "goserv_test"; |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Run the tests: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| export TEST_DATABASE_URL='postgres://postgres:postgres@localhost:5432/goserv_test' |  | ||||||
| go test -mod=vendor ./... |  | ||||||
| ``` |  | ||||||
| 
 | 
 | ||||||
| ## Dependencies | ## Dependencies | ||||||
| 
 | 
 | ||||||
| This setup can be run on a VPS, such as Digital Ocean, OVH, or Scaleway | This setup can be run on a VPS, such as Digital Ocean, OVH, or Scaleway | ||||||
| for \$5/month with minimal dependencies: | for \$5/month with minimal dependencies: | ||||||
| 
 | 
 | ||||||
| - VPS | -   VPS | ||||||
| - Caddy | -   Caddy | ||||||
| - PostgreSQL | -   PostgreSQL | ||||||
| - Serviceman | -   Serviceman | ||||||
| 
 | 
 | ||||||
| **Mac**, **Linux**: | **Mac**, **Linux**: | ||||||
| 
 | 
 | ||||||
| @ -249,7 +80,7 @@ sudo env PATH="$PATH" \ | |||||||
|     caddy run --config ./Caddyfile |     caddy run --config ./Caddyfile | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| See the Cheat Sheets at https://webinstall.dev/caddy | See the Cheat Sheet at https://webinstall.dev/caddy | ||||||
| and https://webinstall.dev/serviceman | and https://webinstall.dev/serviceman | ||||||
| 
 | 
 | ||||||
| ### PostgreSQL (Database) | ### PostgreSQL (Database) | ||||||
| @ -275,14 +106,6 @@ psql 'postgres://postgres:postgres@localhost:5432/postgres' | |||||||
| See the Cheat Sheets at https://webinstall.dev/postgres | See the Cheat Sheets at https://webinstall.dev/postgres | ||||||
| and https://webinstall.dev/serviceman | and https://webinstall.dev/serviceman | ||||||
| 
 | 
 | ||||||
| ## Licenses | ## License | ||||||
| 
 | 
 | ||||||
| Copyright 2020 The GoServ Authors. All rights reserved. | Copyright 2020. All rights reserved. | ||||||
| 
 |  | ||||||
| ### Exceptions |  | ||||||
| 
 |  | ||||||
| - `countries.json` LGPL, taken from <https://github.com/stefangabos/world_countries> |  | ||||||
| - `flags.json` MIT, taken from <https://github.com/matiassingers/emoji-flags> |  | ||||||
| 
 |  | ||||||
| These are probably also in the Public Domain. \ |  | ||||||
| (gathering the official data from any source would yield the same dataset) |  | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| // +build !dev | // +build !dev | ||||||
| 
 | 
 | ||||||
| //go:generate go run -mod vendor github.com/shurcooL/vfsgen/cmd/vfsgendev -source="git.example.com/example/goserv/assets".Assets | //go:generate go run -mod vendor github.com/shurcooL/vfsgen/cmd/vfsgendev -source="git.coolaj86.com/coolaj86/goserv/assets".Assets | ||||||
| 
 | 
 | ||||||
| package assets | package assets | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| // +build !dev | // +build !dev | ||||||
| 
 | 
 | ||||||
| //go:generate go run -mod vendor github.com/shurcooL/vfsgen/cmd/vfsgendev -source="git.example.com/example/goserv/assets/configfs".Assets | //go:generate go run -mod vendor github.com/shurcooL/vfsgen/cmd/vfsgendev -source="git.coolaj86.com/coolaj86/goserv/assets/configfs".Assets | ||||||
| 
 | 
 | ||||||
| package configfs | package configfs | ||||||
|  | |||||||
| @ -1,3 +0,0 @@ | |||||||
| -- this is only used for the tests |  | ||||||
| DROP TABLE IF EXISTS "authn"; |  | ||||||
| DROP TABLE IF EXISTS "events"; |  | ||||||
| @ -8,12 +8,12 @@ CREATE TABLE IF NOT EXISTS "authn" ( | |||||||
|     "id" TEXT PRIMARY KEY DEFAULT gen_random_uuid(), |     "id" TEXT PRIMARY KEY DEFAULT gen_random_uuid(), | ||||||
|     "ppid" TEXT NOT NULL, |     "ppid" TEXT NOT NULL, | ||||||
|     "email" TEXT NOT NULL, |     "email" TEXT NOT NULL, | ||||||
|     "verified_at" TIMESTAMPTZ NOT NULL DEFAULT ('0001-01-01 00:00:00' AT TIME ZONE 'UTC'), |     "verified" BOOL NOT NULL DEFAULT FALSE, | ||||||
|     "created_at" TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), |     "created_at" TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), | ||||||
|     "updated_at" TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), |     "updated_at" TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), | ||||||
|     "deleted_at" TIMESTAMPTZ NOT NULL DEFAULT ('0001-01-01 00:00:00' AT TIME ZONE 'UTC') |     "deleted_at" TIMESTAMP NOT NULL DEFAULT ('epoch' AT TIME ZONE 'UTC') | ||||||
| ); | ); | ||||||
| --CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_ppid" ON "authn" ("ppid"); | --CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_slug" ON "authn" ("ppid"); | ||||||
| CREATE INDEX IF NOT EXISTS "idx_ppid" ON "authn" ("ppid"); | CREATE INDEX IF NOT EXISTS "idx_ppid" ON "authn" ("ppid"); | ||||||
| CREATE INDEX IF NOT EXISTS "idx_email" ON "authn" ("email"); | CREATE INDEX IF NOT EXISTS "idx_email" ON "authn" ("email"); | ||||||
| 
 | 
 | ||||||
| @ -26,8 +26,8 @@ CREATE TABLE IF NOT EXISTS "events" ( | |||||||
|     "table" TEXT NOT NULL, |     "table" TEXT NOT NULL, | ||||||
|     "record" TEXT NOT NULL, |     "record" TEXT NOT NULL, | ||||||
|     "by" TEXT NOT NULL, |     "by" TEXT NOT NULL, | ||||||
|     "at" TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'UTC') |     "at" TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), | ||||||
| ); | ); | ||||||
| --CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_record" ON "events" ("record"); | --CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_record" ON "events" ("record"); | ||||||
| CREATE INDEX IF NOT EXISTS "idx_record" ON "events" ("record"); | CREATE INDEX IF NOT EXISTS "idx_record" ON authn ("record"); | ||||||
| CREATE INDEX IF NOT EXISTS "idx_by" ON "events" ("by"); | CREATE INDEX IF NOT EXISTS "idx_by" ON authn ("by"); | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								doc.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								doc.go
									
									
									
									
									
								
							| @ -1,6 +0,0 @@ | |||||||
| // See also |  | ||||||
| // |  | ||||||
| // internal/api: http://localhost:6060/pkg/git.example.com/example/project/internal/api |  | ||||||
| // |  | ||||||
| // internal/db: http://localhost:6060/pkg/git.example.com/example/project/internal/db |  | ||||||
| package main |  | ||||||
| @ -15,8 +15,6 @@ example.com { | |||||||
| 
 | 
 | ||||||
|     # reverse proxy /api to :3000 |     # reverse proxy /api to :3000 | ||||||
|     reverse_proxy /api/* localhost:3000 |     reverse_proxy /api/* localhost:3000 | ||||||
|     reverse_proxy /.well-known/openid-configuration localhost:3000 |  | ||||||
|     reverse_proxy /.well-known/jwks.json localhost:3000 |  | ||||||
| 
 | 
 | ||||||
|     # serve static files from public folder, but not /api |     # serve static files from public folder, but not /api | ||||||
|     @notApi { |     @notApi { | ||||||
| @ -24,8 +22,6 @@ example.com { | |||||||
|             try_files {path} {path}/ {path}/index.html |             try_files {path} {path}/ {path}/index.html | ||||||
|         } |         } | ||||||
|         not path /api/* |         not path /api/* | ||||||
|         not path /.well-known/openid-configuration |  | ||||||
|         not path /.well-known/jwks.json |  | ||||||
|     } |     } | ||||||
|     route { |     route { | ||||||
|       rewrite @notApi {http.matchers.file.relative} |       rewrite @notApi {http.matchers.file.relative} | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								examples/build.sh
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/build.sh
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -8,3 +8,4 @@ go mod tidy | |||||||
| go mod vendor | go mod vendor | ||||||
| go generate -mod=vendor ./... | go generate -mod=vendor ./... | ||||||
| go build -mod=vendor . | go build -mod=vendor . | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -1,22 +1,2 @@ | |||||||
| PORT="3000" | PORT="3000" | ||||||
| #LISTEN=":3000" | #LISTEN=":3000" | ||||||
| DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres |  | ||||||
| TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres_test |  | ||||||
| 
 |  | ||||||
| TRUST_PROXY=false |  | ||||||
| 
 |  | ||||||
| # Supports OIDC-compliant SSO issuers / providers |  | ||||||
| # (Auth0, Google, etc) |  | ||||||
| # (should provide .well-known/openid-configuration and .well-known/jwks.json) |  | ||||||
| OIDC_WHITELIST=https://mock.pocketid.app |  | ||||||
| 
 |  | ||||||
| # Public Key may be provided in addition to or in lieu of OIDC_WHITELIST |  | ||||||
| # can be RSA or ECDSA, either a filename or JWK/JSON (or PEM, but good luck escaping the newlines) |  | ||||||
| # |  | ||||||
| #   go install -mod=vendor git.rootprojects.org/root/keypairs/cmd/keypairs |  | ||||||
| #   keypairs gen -o priv.jwk.json --pub pub.jwk.json |  | ||||||
| # |  | ||||||
| #PUBLIC_KEY='{"crv":"P-256","kid":"kN4qj1w01Ry6ElG9I3qAVJOZFYLDklPFUdHaKozWtmc","kty":"EC","use":"sig","x":"SzzNgrOM_N0GwQWZPGFcdIKmfoQD6aXIzYm4gzGyPgQ","y":"erYeb884pk0BGMewDzEh_qYDB0aOFIxFjrXdqIzkmbw"}' |  | ||||||
| PUBLIC_KEY=./pub.jwk.json |  | ||||||
| 
 |  | ||||||
| #--demo is explicit |  | ||||||
|  | |||||||
| @ -1,5 +0,0 @@ | |||||||
| # Install `keypairs` |  | ||||||
| go install -mod=vendor git.rootprojects.org/root/keypairs/cmd/keypairs |  | ||||||
| 
 |  | ||||||
| # Generate a keypair |  | ||||||
| keypairs gen -o key.jwk.json --pub pub.jwk.json |  | ||||||
| @ -1,4 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| bash ./examples/build.sh |  | ||||||
| bash ./examples/genkeys.sh |  | ||||||
| bash ./examples/test.sh |  | ||||||
| @ -1,45 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| 
 |  | ||||||
| export BASE_URL=http://localhost:7070 |  | ||||||
| #export BASE_URL=https://example.com |  | ||||||
| #CURL_OPTS="-sS" |  | ||||||
| CURL_OPTS="" |  | ||||||
| 
 |  | ||||||
| mkdir -p ./tmp/ |  | ||||||
| 
 |  | ||||||
| # Sign an Admin token |  | ||||||
| echo '{ "sub": "admin_ppid", "email": "me@example.com", "iss": "'"${BASE_URL}"'" }' > ./tmp/admin.claims.json |  | ||||||
| keypairs sign --exp 1h ./key.jwk.json ./tmp/admin.claims.json > ./tmp/admin.jwt.txt 2> ./tmp/admin.jws.json |  | ||||||
| export ADMIN_TOKEN=$(cat ./tmp/admin.jwt.txt) |  | ||||||
| 
 |  | ||||||
| # verify the Admin token |  | ||||||
| #keypairs verify ./pub.jwk.json ./admin.jwt.txt |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # Sign a User token |  | ||||||
| echo '{ "sub": "random_ppid", "email": "me@example.com", "iss": "'"${BASE_URL}"'" }' > ./tmp/user.claims.json |  | ||||||
| keypairs sign --exp 1h ./key.jwk.json ./tmp/user.claims.json > ./tmp/user.jwt.txt 2> ./tmp/user.jws.json |  | ||||||
| export USER_TOKEN=$(cat ./tmp/user.jwt.txt) |  | ||||||
| 
 |  | ||||||
| # verify the User token |  | ||||||
| #keypairs verify ./pub.jwk.json ./user.jwt.txt |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| EID=$(cat ./user.jws.json | grep sub | cut -d'"' -f 4) |  | ||||||
| 
 |  | ||||||
| echo "" |  | ||||||
| echo 'DELETE /api/public/reset (only works in --demo mode, deletes all data)' |  | ||||||
| curl $CURL_OPTS -X DELETE "${BASE_URL}/api/public/reset" |  | ||||||
| echo "" |  | ||||||
| 
 |  | ||||||
| echo "" |  | ||||||
| echo "Bootstrap with a new admin (only works once)" |  | ||||||
| curl -f $CURL_OPTS -X POST "${BASE_URL}/api/public/setup" \ |  | ||||||
|     -H "Authorization: Bearer ${ADMIN_TOKEN}" |  | ||||||
| echo "" |  | ||||||
| 
 |  | ||||||
| echo "Create a new user" |  | ||||||
| curl $CURL_OPTS -X POST "${BASE_URL}/api/users" \ |  | ||||||
|     -H "Authorization: Bearer ${USER_TOKEN}" \ |  | ||||||
|     -d '{ "display_name": "Jo Doe" }' |  | ||||||
| echo "" |  | ||||||
							
								
								
									
										7
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.mod
									
									
									
									
									
								
							| @ -1,17 +1,14 @@ | |||||||
| module git.example.com/example/goserv | module git.coolaj86.com/coolaj86/goserv | ||||||
| 
 | 
 | ||||||
| go 1.15 | go 1.15 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	git.rootprojects.org/root/go-gitver v1.1.3 // indirect |  | ||||||
| 	git.rootprojects.org/root/go-gitver/v2 v2.0.1 |  | ||||||
| 	git.rootprojects.org/root/keypairs v0.6.3 |  | ||||||
| 	github.com/go-chi/chi v4.1.2+incompatible | 	github.com/go-chi/chi v4.1.2+incompatible | ||||||
| 	github.com/jmoiron/sqlx v1.2.0 | 	github.com/jmoiron/sqlx v1.2.0 | ||||||
| 	github.com/joho/godotenv v1.3.0 | 	github.com/joho/godotenv v1.3.0 | ||||||
| 	github.com/lib/pq v1.8.0 | 	github.com/lib/pq v1.8.0 | ||||||
| 	github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect | 	github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect | ||||||
| 	github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 | 	github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 | ||||||
| 	golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b | 	golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346 // indirect | ||||||
| 	google.golang.org/appengine v1.6.6 // indirect | 	google.golang.org/appengine v1.6.6 // indirect | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.sum
									
									
									
									
									
								
							| @ -1,9 +1,3 @@ | |||||||
| git.rootprojects.org/root/go-gitver v1.1.3 h1:/qR9z53vY+IFhWRxLkF9cjaiWh8xRJIm6gyuW+MG81A= |  | ||||||
| git.rootprojects.org/root/go-gitver v1.1.3/go.mod h1:Rj1v3TBhvdaSphFEqMynUYwAz/4f+wY/+syBTvRrmlI= |  | ||||||
| git.rootprojects.org/root/go-gitver/v2 v2.0.1 h1:CdNfvlGDggFbyImxlqA2eFUVRKQKn1EJNk7w/3TQAfk= |  | ||||||
| git.rootprojects.org/root/go-gitver/v2 v2.0.1/go.mod h1:ur82M/jZcvr1WWihyVtNEgDBqIjo22o56wcVHeVJFh8= |  | ||||||
| git.rootprojects.org/root/keypairs v0.6.3 h1:dFuDjlg9KFXhRTIyVy3VfXzlX7hyyeC0J8PTUUZBzpo= |  | ||||||
| git.rootprojects.org/root/keypairs v0.6.3/go.mod h1:WGI8PadOp+4LjUuI+wNlSwcJwFtY8L9XuNjuO3213HA= |  | ||||||
| github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= | github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= | ||||||
| github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= | github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= | ||||||
| github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= | github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= | ||||||
| @ -26,7 +20,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec | |||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= |  | ||||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
| golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||||
| @ -44,11 +37,10 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | |||||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b h1:07IVqnnzaip3TGyl/cy32V5YP3FguWG4BybYDTBNpm0= | golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346 h1:hzJjkvxUIF3bSt+v8N5tBQNx/605vszZJ+3XsIamzZo= | ||||||
| golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= | golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= | ||||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= |  | ||||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= | google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= | ||||||
| google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
|  | |||||||
| @ -1,414 +0,0 @@ | |||||||
| package api |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"crypto/rand" |  | ||||||
| 	"database/sql" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.rootprojects.org/root/keypairs" |  | ||||||
| 	"git.rootprojects.org/root/keypairs/keyfetch" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-chi/chi" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // TrustProxy will respect X-Forwarded-* headers |  | ||||||
| var TrustProxy bool |  | ||||||
| 
 |  | ||||||
| // OIDCWhitelist is a list of allowed issuers |  | ||||||
| var OIDCWhitelist string |  | ||||||
| var issuers keyfetch.Whitelist |  | ||||||
| 
 |  | ||||||
| // RandReader is a crypto/rand.Reader by default |  | ||||||
| var RandReader io.Reader = rand.Reader |  | ||||||
| 
 |  | ||||||
| var startedAt = time.Now() |  | ||||||
| var defaultMaxBytes int64 = 1 << 20 |  | ||||||
| var apiIsReady bool |  | ||||||
| 
 |  | ||||||
| // Init will add the API routes to the given router |  | ||||||
| func Init(pub keypairs.PublicKey, r chi.Router) http.Handler { |  | ||||||
| 
 |  | ||||||
| 	// TODO more of this stuff should be options for the API |  | ||||||
| 	{ |  | ||||||
| 		// block-scoped so we don't keep temp vars around |  | ||||||
| 		var err error |  | ||||||
| 		list := strings.Fields(strings.ReplaceAll(strings.TrimSpace(OIDCWhitelist), ",", " ")) |  | ||||||
| 		issuers, err = keyfetch.NewWhitelist(list) |  | ||||||
| 		if nil != err { |  | ||||||
| 			log.Fatal("error parsing oidc whitelist:", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// OIDC Routes |  | ||||||
| 	if nil != pub { |  | ||||||
| 		fmt.Println("Public Key Thumbprint:", pub.Thumbprint()) |  | ||||||
| 		fmt.Println("OIDC enabled at /.well-known/openid-configuration") |  | ||||||
| 		r.Get("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			baseURL := getBaseURL(r) |  | ||||||
| 			w.Header().Set("Content-Type", "application/json") |  | ||||||
| 			w.Write([]byte(fmt.Sprintf( |  | ||||||
| 				`{ "issuer": "%s", "jwks_uri": "%s/.well-known/jwks.json" }`+"\n", |  | ||||||
| 				baseURL, baseURL, |  | ||||||
| 			))) |  | ||||||
| 		}) |  | ||||||
| 		fmt.Println("JWKs enabled at /.well-known/jwks.json") |  | ||||||
| 		r.Get("/.well-known/jwks.json", func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			w.Header().Set("Content-Type", "application/json") |  | ||||||
| 
 |  | ||||||
| 			// non-standard: add expiry for when key should be fetched again |  | ||||||
| 			// TODO expiry should also go in the HTTP caching headers |  | ||||||
| 			exp := time.Now().Add(2 * time.Hour) |  | ||||||
| 			b := pubToOIDC(pub, exp) |  | ||||||
| 
 |  | ||||||
| 			// it's the little things |  | ||||||
| 			w.Write(append(b, '\n')) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r.Route("/api", func(r chi.Router) { |  | ||||||
| 		r.Use(limitResponseSize) |  | ||||||
| 		r.Use(jsonAllTheThings) |  | ||||||
| 
 |  | ||||||
| 		/* |  | ||||||
| 			n, err := countAdmins() |  | ||||||
| 			if nil != err { |  | ||||||
| 				log.Fatal("could not connect to database on boot:", err) |  | ||||||
| 			} |  | ||||||
| 			apiIsReady = n > 0 |  | ||||||
| 		*/ |  | ||||||
| 
 |  | ||||||
| 		// Unauthenticated routes |  | ||||||
| 		r.Route("/public", func(r chi.Router) { |  | ||||||
| 			r.Post("/setup", publicSetup) |  | ||||||
| 
 |  | ||||||
| 			r.Get("/ping", ping) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		// Require admin-level permission |  | ||||||
| 		r.Route("/admin", func(r chi.Router) { |  | ||||||
| 			r.Use(errorUnlessReady()) |  | ||||||
| 			r.Use(mustAdmin()) |  | ||||||
| 
 |  | ||||||
| 			r.Get("/ping", ping) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		// Any authenticated user |  | ||||||
| 		r.Route("/user", func(r chi.Router) { |  | ||||||
| 			r.Use(errorUnlessReady()) |  | ||||||
| 			r.Use(canImpersonate()) |  | ||||||
| 			r.Get("/ping", ping) |  | ||||||
| 
 |  | ||||||
| 			// TODO get ALL of the user's data |  | ||||||
| 			//r.Get("/", userComplete) |  | ||||||
| 		}) |  | ||||||
| 	}) |  | ||||||
| 
 |  | ||||||
| 	return r |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Reset sets the API back to its initial state |  | ||||||
| func Reset() { |  | ||||||
| 	apiIsReady = false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // utils |  | ||||||
| 
 |  | ||||||
| func getBaseURL(r *http.Request) string { |  | ||||||
| 	var scheme string |  | ||||||
| 	if nil != r.TLS || |  | ||||||
| 		(TrustProxy && "https" == r.Header.Get("X-Forwarded-Proto")) { |  | ||||||
| 		scheme = "https:" |  | ||||||
| 	} else { |  | ||||||
| 		scheme = "http:" |  | ||||||
| 	} |  | ||||||
| 	return fmt.Sprintf( |  | ||||||
| 		"%s//%s", |  | ||||||
| 		scheme, |  | ||||||
| 		r.Host, |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func mustAuthn(r *http.Request) (*http.Request, error) { |  | ||||||
| 	authzParts := strings.Split(r.Header.Get("Authorization"), " ") |  | ||||||
| 	if 2 != len(authzParts) { |  | ||||||
| 		return nil, fmt.Errorf("Bad Request: missing Auhorization header") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jwt := authzParts[1] |  | ||||||
| 	// TODO should probably add an error to keypairs |  | ||||||
| 	jws := keypairs.JWTToJWS(jwt) |  | ||||||
| 	if nil == jws { |  | ||||||
| 		return nil, fmt.Errorf("Bad Request: malformed Authorization header") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := jws.DecodeComponents(); nil != err { |  | ||||||
| 		return nil, fmt.Errorf("Bad Request: malformed JWT") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	kid, _ := jws.Header["kid"].(string) |  | ||||||
| 	if "" == kid { |  | ||||||
| 		return nil, fmt.Errorf("Bad Request: missing 'kid' identifier") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	iss, _ := jws.Claims["iss"].(string) |  | ||||||
| 	// TODO beware domain fronting, we should set domain statically |  | ||||||
| 	// See https://pkg.go.dev/git.rootprojects.org/root/keypairs@v0.6.2/keyfetch |  | ||||||
| 	// (Caddy does protect against Domain-Fronting by default: |  | ||||||
| 	//     https://github.com/caddyserver/caddy/issues/2500) |  | ||||||
| 	if "" == iss || !issuers.IsTrustedIssuer(iss, r) { |  | ||||||
| 		return nil, fmt.Errorf("Bad Request: 'iss' is not a trusted issuer") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	pub, err := keyfetch.OIDCJWK(kid, iss) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, fmt.Errorf("Bad Request: 'kid' could not be matched to a known public key") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	errs := keypairs.VerifyClaims(pub, jws) |  | ||||||
| 	if nil != errs { |  | ||||||
| 		strs := []string{} |  | ||||||
| 		for _, err := range errs { |  | ||||||
| 			strs = append(strs, err.Error()) |  | ||||||
| 		} |  | ||||||
| 		return nil, fmt.Errorf("invalid jwt:\n%s", strings.Join(strs, "\n\t")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	email, _ := jws.Claims["email"].(string) |  | ||||||
| 	ppid, _ := jws.Claims["sub"].(string) |  | ||||||
| 	if "" == email || "" == ppid { |  | ||||||
| 		return nil, fmt.Errorf("valid signed token, but missing claim for either 'email' or 'sub'") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ctx := context.WithValue(r.Context(), userPPID, ppid) |  | ||||||
| 	return r.WithContext(ctx), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func pubToOIDC(pub keypairs.PublicKey, exp time.Time) []byte { |  | ||||||
| 	exps := strconv.FormatInt(exp.Unix(), 10) |  | ||||||
| 	jsons := string(keypairs.MarshalJWKPublicKey(pub)) |  | ||||||
| 
 |  | ||||||
| 	// this isn't as fragile as it looks, just adding some OIDC keys and such |  | ||||||
| 
 |  | ||||||
| 	// make prettier |  | ||||||
| 	jsons = strings.Replace(jsons, `{"`, `{ "`, 1) |  | ||||||
| 	jsons = strings.Replace(jsons, `",`, `" ,`, -1) |  | ||||||
| 
 |  | ||||||
| 	// nix trailing } |  | ||||||
| 	jsons = jsons[0 : len(jsons)-1] |  | ||||||
| 	// add on the OIDC stuff (exp is non-standard, but used by pocketid) |  | ||||||
| 	jsons = `{ "keys": [ ` + |  | ||||||
| 		jsons + fmt.Sprintf(`, "ext": true , "key_ops": ["verify"], "exp": %s }`, exps) + |  | ||||||
| 		" ] }" |  | ||||||
| 
 |  | ||||||
| 	return []byte(jsons) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // HTTPResponse gives a basic status message |  | ||||||
| // TODO sanitize all error messages and define error codes |  | ||||||
| type HTTPResponse struct { |  | ||||||
| 	Error   string      `json:"error,omitempty"` |  | ||||||
| 	Code    string      `json:"code,omitempty"` |  | ||||||
| 	Success bool        `json:"success"` |  | ||||||
| 	Data    interface{} `json:"result,omitempty"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func ping(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 	ctx := r.Context() |  | ||||||
| 	// as of yet, only known to be a user |  | ||||||
| 	ppid, _ := ctx.Value(userPPID).(string) |  | ||||||
| 
 |  | ||||||
| 	w.Write([]byte(fmt.Sprintf( |  | ||||||
| 		`{ "success": true, "uptime": %.0f, "ppid": %q }`+"\n", |  | ||||||
| 		time.Since(startedAt).Seconds(), ppid, |  | ||||||
| 	))) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func noImpl(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 	w.Write([]byte( |  | ||||||
| 		`{ "success": false, "error": "not implemented" }` + "\n", |  | ||||||
| 	)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func publicSetup(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 	if apiIsReady { |  | ||||||
| 		// default is already 404, methinks |  | ||||||
| 		http.Error(w, "Not Found", http.StatusNotFound) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r, err := mustAuthn(r) |  | ||||||
| 	if nil != err { |  | ||||||
| 		userError(w, err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ctx := r.Context() |  | ||||||
| 	ppid := ctx.Value(userPPID).(string) |  | ||||||
| 	if "" == ppid { |  | ||||||
| 		if nil != err { |  | ||||||
| 			userError(w, err) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := adminBootstrap(ppid); nil != err { |  | ||||||
| 		serverError("publicSetup.bootstrap", w, err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	apiIsReady = true |  | ||||||
| 
 |  | ||||||
| 	w.Write([]byte(fmt.Sprintf( |  | ||||||
| 		`{ "success": true, "sub": %q }`+"\n", |  | ||||||
| 		ppid, |  | ||||||
| 	))) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func reply(w http.ResponseWriter, msg interface{}) { |  | ||||||
| 	w.WriteHeader(http.StatusOK) |  | ||||||
| 	b, _ := json.MarshalIndent(&HTTPResponse{ |  | ||||||
| 		Success: true, |  | ||||||
| 		Data:    msg, |  | ||||||
| 	}, "", "  ") |  | ||||||
| 	w.Write(append(b, '\n')) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func userError(w http.ResponseWriter, err error) { |  | ||||||
| 	w.WriteHeader(http.StatusBadRequest) |  | ||||||
| 	b, _ := json.Marshal(&HTTPResponse{ |  | ||||||
| 		Error: err.Error(), |  | ||||||
| 	}) |  | ||||||
| 	w.Write(append(b, '\n')) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func dbError(hint string, w http.ResponseWriter, err error) { |  | ||||||
| 	serverError(hint, w, err) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func serverError(hint string, w http.ResponseWriter, err error) { |  | ||||||
| 	// TODO check constraint errors and such, as those are likely user errors |  | ||||||
| 	if sql.ErrNoRows == err { |  | ||||||
| 		userError(w, fmt.Errorf("E_NOT_FOUND: %s", err)) |  | ||||||
| 		return |  | ||||||
| 	} else if strings.Contains(err.Error(), "constraint") { |  | ||||||
| 		userError(w, fmt.Errorf("E_DUPLICATE_NAME: %s", err)) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	log.Printf("[%s] error: %v", hint, err) |  | ||||||
| 	w.WriteHeader(http.StatusInternalServerError) |  | ||||||
| 	b, _ := json.Marshal(&HTTPResponse{ |  | ||||||
| 		Error: err.Error(), |  | ||||||
| 	}) |  | ||||||
| 	w.Write(append(b, '\n')) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var errSetID = errors.New("you may not set the player's ID") |  | ||||||
| 
 |  | ||||||
| // Middleware |  | ||||||
| 
 |  | ||||||
| func errorUnlessReady() func(next http.Handler) http.Handler { |  | ||||||
| 	return func(next http.Handler) http.Handler { |  | ||||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			if !apiIsReady { |  | ||||||
| 				http.Error(w, "Not Found", http.StatusNotFound) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			next.ServeHTTP(w, r) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type adminCtx string |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	adminPPID adminCtx = "ppid" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type userCtx string |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	userPPID userCtx = "ppid" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func canImpersonate() func(next http.Handler) http.Handler { |  | ||||||
| 	return actAsAdmin(false) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func mustAdmin() func(next http.Handler) http.Handler { |  | ||||||
| 	return actAsAdmin(true) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func countAdmins() (int, error) { |  | ||||||
| 	return 0, errors.New("not implemented") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func adminBootstrap(ppid string) error { |  | ||||||
| 	return errors.New("not implemented") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func actAsAdmin(must bool) func(next http.Handler) http.Handler { |  | ||||||
| 	return func(next http.Handler) http.Handler { |  | ||||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			var err error |  | ||||||
| 			r, err = mustAuthn(r) |  | ||||||
| 			if nil != err { |  | ||||||
| 				userError(w, err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			ctx := r.Context() |  | ||||||
| 			// as of yet, only known to be a user |  | ||||||
| 			ppid, _ := ctx.Value(userPPID).(string) |  | ||||||
| 
 |  | ||||||
| 			//ok, err := isAdmin(ppid) |  | ||||||
| 			ok, err := false, errors.New("not implemented") |  | ||||||
| 			if nil != err { |  | ||||||
| 				serverError("actAsAdmin", w, err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			if !ok { |  | ||||||
| 				if must { |  | ||||||
| 					userError(w, errors.New("you're not an admin")) |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 				next.ServeHTTP(w, r) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// we now know this is an admin, adjust accordingly |  | ||||||
| 			ctx = context.WithValue(r.Context(), adminPPID, ppid) |  | ||||||
| 
 |  | ||||||
| 			// also, an admin can impersonate |  | ||||||
| 			//uemail := r.URL.Query().Get("user_email") |  | ||||||
| 			uppid := r.URL.Query().Get("manager_id") |  | ||||||
| 			if "" == uppid { |  | ||||||
| 				uppid = ppid |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			ctx = context.WithValue(ctx, userPPID, uppid) |  | ||||||
| 			next.ServeHTTP(w, r.WithContext(ctx)) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func jsonAllTheThings(next http.Handler) http.Handler { |  | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 		// just setting a default, other handlers can change this |  | ||||||
| 		w.Header().Set("Content-Type", "application/json") |  | ||||||
| 		next.ServeHTTP(w, r) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func limitResponseSize(next http.Handler) http.Handler { |  | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 		r.Body = http.MaxBytesReader(w, r.Body, defaultMaxBytes) |  | ||||||
| 		next.ServeHTTP(w, r) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @ -1,164 +0,0 @@ | |||||||
| package api |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"log" |  | ||||||
| 	mathrand "math/rand" |  | ||||||
| 	"net/http" |  | ||||||
| 	"net/http/httptest" |  | ||||||
| 	"net/url" |  | ||||||
| 	"os" |  | ||||||
| 	"strings" |  | ||||||
| 	"testing" |  | ||||||
| 
 |  | ||||||
| 	"git.example.com/example/goserv/internal/db" |  | ||||||
| 	"git.rootprojects.org/root/keypairs" |  | ||||||
| 	"git.rootprojects.org/root/keypairs/keyfetch" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-chi/chi" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var srv *httptest.Server |  | ||||||
| 
 |  | ||||||
| var testKey keypairs.PrivateKey |  | ||||||
| var testPub keypairs.PublicKey |  | ||||||
| var testWhitelist keyfetch.Whitelist |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	// In tests it's nice to get the same "random" values, every time |  | ||||||
| 	RandReader = testReader{} |  | ||||||
| 	mathrand.Seed(0) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestMain(m *testing.M) { |  | ||||||
| 	connStr := needsTestDB(m) |  | ||||||
| 	if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") { |  | ||||||
| 		connStr += "?sslmode=disable" |  | ||||||
| 	} else { |  | ||||||
| 		connStr += "?sslmode=required" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := db.Init(connStr); nil != err { |  | ||||||
| 		log.Fatal("db connection error", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if err := db.DropAllTables(db.PleaseDoubleCheckTheDatabaseURLDontDropProd(connStr)); nil != err { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	if err := db.Init(connStr); nil != err { |  | ||||||
| 		log.Fatal("db connection error", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var err error |  | ||||||
| 	testKey = keypairs.NewDefaultPrivateKey() |  | ||||||
| 	testPub = keypairs.NewPublicKey(testKey.Public()) |  | ||||||
| 	r := chi.NewRouter() |  | ||||||
| 	srv = httptest.NewServer(Init(testPub, r)) |  | ||||||
| 	testWhitelist, err = keyfetch.NewWhitelist(nil, []string{srv.URL}) |  | ||||||
| 	if nil != err { |  | ||||||
| 		log.Fatal("bad whitelist", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	os.Exit(m.Run()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // public APIs |  | ||||||
| 
 |  | ||||||
| func Test_Public_Ping(t *testing.T) { |  | ||||||
| 	if err := testPing("public"); nil != err { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // test types |  | ||||||
| 
 |  | ||||||
| type testReader struct{} |  | ||||||
| 
 |  | ||||||
| func (testReader) Read(p []byte) (n int, err error) { |  | ||||||
| 	return mathrand.Read(p) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func testPing(which string) error { |  | ||||||
| 	urlstr := fmt.Sprintf("/api/%s/ping", which) |  | ||||||
| 	res, err := testReq("GET", urlstr, "", nil, 200) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	data := map[string]interface{}{} |  | ||||||
| 	if err := json.NewDecoder(res.Body).Decode(&data); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if success, ok := data["success"].(bool); !ok || !success { |  | ||||||
| 		log.Printf("Bad Response\n\tURL:%s\n\tBody:\n%#v", urlstr, data) |  | ||||||
| 		return errors.New("bad response: missing success") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if ppid, _ := data["ppid"].(string); "" != ppid { |  | ||||||
| 		return fmt.Errorf("the effective user ID isn't what it should be: %q != %q", ppid, "") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func testReq(method, pathname string, jwt string, payload []byte, expectedStatus int) (*http.Response, error) { |  | ||||||
| 	client := srv.Client() |  | ||||||
| 	urlstr, _ := url.Parse(srv.URL + pathname) |  | ||||||
| 
 |  | ||||||
| 	if "" == method { |  | ||||||
| 		method = "GET" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	req := &http.Request{ |  | ||||||
| 		Method: method, |  | ||||||
| 		URL:    urlstr, |  | ||||||
| 		Body:   ioutil.NopCloser(bytes.NewReader(payload)), |  | ||||||
| 		Header: http.Header{}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(jwt) > 0 { |  | ||||||
| 		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", jwt)) |  | ||||||
| 	} |  | ||||||
| 	res, err := client.Do(req) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	if expectedStatus > 0 { |  | ||||||
| 		if expectedStatus != res.StatusCode { |  | ||||||
| 			data, _ := ioutil.ReadAll(res.Body) |  | ||||||
| 			log.Printf("Bad Response: %d\n\tURL:%s\n\tBody:\n%s", res.StatusCode, urlstr, string(data)) |  | ||||||
| 			return nil, fmt.Errorf("bad status code: %d", res.StatusCode) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return res, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func needsTestDB(m *testing.M) string { |  | ||||||
| 	connStr := os.Getenv("TEST_DATABASE_URL") |  | ||||||
| 	if "" == connStr { |  | ||||||
| 		log.Fatal(`no connection string defined |  | ||||||
| 
 |  | ||||||
| You must set TEST_DATABASE_URL to run db tests. |  | ||||||
| 
 |  | ||||||
| You may find this helpful: |  | ||||||
| 
 |  | ||||||
|     psql 'postgres://postgres:postgres@localhost:5432/postgres' |  | ||||||
| 
 |  | ||||||
| 	DROP DATABASE IF EXISTS postgres_test; |  | ||||||
| 	CREATE DATABASE postgres_test; |  | ||||||
| 	\q |  | ||||||
| 
 |  | ||||||
| Then your test database URL will be |  | ||||||
| 
 |  | ||||||
|     export TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres_test |  | ||||||
| `) |  | ||||||
| 	} |  | ||||||
| 	return connStr |  | ||||||
| } |  | ||||||
| @ -1,37 +0,0 @@ | |||||||
| package api |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.example.com/example/goserv/internal/db" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func newID() string { |  | ||||||
| 	// Postgres returns IDs on inserts but, |  | ||||||
| 	// for portability and ease of association, |  | ||||||
| 	// we'll create our own. |  | ||||||
| 	b := make([]byte, 16) |  | ||||||
| 	_, _ = RandReader.Read(b) |  | ||||||
| 	id := base64.RawURLEncoding.EncodeToString(b) |  | ||||||
| 	return id |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NotDeleted supplements a WHERE clause |  | ||||||
| const NotDeleted = ` |  | ||||||
|  ( "deleted_at" IS NULL OR "deleted_at" = '0001-01-01 00:00:00+00' OR "deleted_at" = '1970-01-01 00:00:00+00' )  |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| func logEvent(action, table, recordID, by string, at time.Time) (string, error) { |  | ||||||
| 	id := newID() |  | ||||||
| 
 |  | ||||||
| 	if _, err := db.DB.Exec(` |  | ||||||
| 		INSERT INTO "events" ("id", "action", "table", "record", "by", "at") |  | ||||||
| 		VALUES ($1, $2, $3, $4, $5, $6)`, |  | ||||||
| 		id, action, table, recordID, by, at, |  | ||||||
| 	); nil != err { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return id, nil |  | ||||||
| } |  | ||||||
| @ -3,13 +3,10 @@ package db | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"database/sql" | 	"database/sql" | ||||||
| 	"fmt" |  | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"regexp" |  | ||||||
| 	"sort" |  | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"git.example.com/example/goserv/assets/configfs" | 	"git.coolaj86.com/coolaj86/goserv/assets/configfs" | ||||||
| 	"github.com/jmoiron/sqlx" | 	"github.com/jmoiron/sqlx" | ||||||
| 
 | 
 | ||||||
| 	// pq injects itself into sql as 'postgres' | 	// pq injects itself into sql as 'postgres' | ||||||
| @ -18,14 +15,21 @@ import ( | |||||||
| 
 | 
 | ||||||
| // DB is a concurrency-safe db connection instance | // DB is a concurrency-safe db connection instance | ||||||
| var DB *sqlx.DB | var DB *sqlx.DB | ||||||
| var firstDBURL PleaseDoubleCheckTheDatabaseURLDontDropProd |  | ||||||
| 
 | 
 | ||||||
| // Init initializes the database | // Init returns a, you guessed it, New Store | ||||||
| func Init(pgURL string) error { | func Init(pgURL string) error { | ||||||
| 	// https://godoc.org/github.com/lib/pq | 	// https://godoc.org/github.com/lib/pq | ||||||
| 
 | 
 | ||||||
| 	firstDBURL = PleaseDoubleCheckTheDatabaseURLDontDropProd(pgURL) | 	f, err := configfs.Assets.Open("./postgres/init.sql") | ||||||
|  | 	if nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	dbtype := "postgres" | 	dbtype := "postgres" | ||||||
|  | 	sqlBytes, err := ioutil.ReadAll(f) | ||||||
|  | 	if nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx, done := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) | 	ctx, done := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) | ||||||
| 	defer done() | 	defer done() | ||||||
| @ -33,29 +37,6 @@ func Init(pgURL string) error { | |||||||
| 	if err := db.PingContext(ctx); nil != err { | 	if err := db.PingContext(ctx); nil != err { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	// basic stuff |  | ||||||
| 	f, err := configfs.Assets.Open("./postgres/init.sql") |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	sqlBytes, err := ioutil.ReadAll(f) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if _, err := db.ExecContext(ctx, string(sqlBytes)); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// project-specific stuff |  | ||||||
| 	f, err = configfs.Assets.Open("./postgres/tables.sql") |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	sqlBytes, err = ioutil.ReadAll(f) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if _, err := db.ExecContext(ctx, string(sqlBytes)); nil != err { | 	if _, err := db.ExecContext(ctx, string(sqlBytes)); nil != err { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -64,58 +45,3 @@ func Init(pgURL string) error { | |||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // PleaseDoubleCheckTheDatabaseURLDontDropProd is just a friendly, |  | ||||||
| // hopefully helpful reminder, not to only use this in test files, |  | ||||||
| // and to not drop the production database |  | ||||||
| type PleaseDoubleCheckTheDatabaseURLDontDropProd string |  | ||||||
| 
 |  | ||||||
| // DropAllTables runs drop.sql, which is intended only for tests |  | ||||||
| func DropAllTables(dbURL PleaseDoubleCheckTheDatabaseURLDontDropProd) error { |  | ||||||
| 	if err := CanDropAllTables(string(dbURL)); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// drop stuff |  | ||||||
| 	f, err := configfs.Assets.Open("./postgres/drop.sql") |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	sqlBytes, err := ioutil.ReadAll(f) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	ctx, done := context.WithDeadline(context.Background(), time.Now().Add(1*time.Second)) |  | ||||||
| 	defer done() |  | ||||||
| 	if _, err := DB.ExecContext(ctx, string(sqlBytes)); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // CanDropAllTables returns an error if the dbURL does not contain the words "test" or |  | ||||||
| // "demo" at a letter boundary |  | ||||||
| func CanDropAllTables(dbURL string) error { |  | ||||||
| 	var isDemo bool |  | ||||||
| 	nonalpha := regexp.MustCompile(`[^a-zA-Z]`) |  | ||||||
| 	haystack := nonalpha.Split(dbURL, -1) |  | ||||||
| 	sort.Strings(haystack) |  | ||||||
| 	for _, needle := range []string{"test", "demo"} { |  | ||||||
| 		// the index to insert x if x is not present (it could be len(a)) |  | ||||||
| 		// (meaning that it is the index at which it exists, if it exists) |  | ||||||
| 		i := sort.SearchStrings(haystack, needle) |  | ||||||
| 		if i < len(haystack) && haystack[i] == needle { |  | ||||||
| 			isDemo = true |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if isDemo { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return fmt.Errorf( |  | ||||||
| 		"test and demo database URLs must contain the word 'test' or 'demo' "+ |  | ||||||
| 			"separated by a non-alphabet character, such as /test2/db_demo1\n%q\n", |  | ||||||
| 		dbURL, |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,66 +0,0 @@ | |||||||
| package db |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"strings" |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func TestMain(m *testing.M) { |  | ||||||
| 	if err := testConnectAndInit(); nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, err.Error()) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	os.Exit(m.Run()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func needsTestDB() (string, error) { |  | ||||||
| 	connStr := os.Getenv("TEST_DATABASE_URL") |  | ||||||
| 	if "" == connStr { |  | ||||||
| 		return "", errors.New(`no connection string defined |  | ||||||
| 
 |  | ||||||
| You must set TEST_DATABASE_URL to run db tests. |  | ||||||
| 
 |  | ||||||
| You may find this helpful: |  | ||||||
| 
 |  | ||||||
|     psql 'postgres://postgres:postgres@localhost:5432/postgres' |  | ||||||
| 
 |  | ||||||
| 	DROP DATABASE IF EXISTS postgres_test; |  | ||||||
| 	CREATE DATABASE postgres_test; |  | ||||||
| 	\q |  | ||||||
| 
 |  | ||||||
| Then your test database URL will be |  | ||||||
| 
 |  | ||||||
|     export TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres_test`) |  | ||||||
| 	} |  | ||||||
| 	return connStr, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func testConnectAndInit() error { |  | ||||||
| 	connStr, err := needsTestDB() |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") { |  | ||||||
| 		connStr += "?sslmode=disable" |  | ||||||
| 	} else { |  | ||||||
| 		connStr += "?sslmode=required" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := Init(connStr); nil != err { |  | ||||||
| 		return fmt.Errorf("db connection error: %w", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDropAll(t *testing.T) { |  | ||||||
| 	connStr := os.Getenv("TEST_DATABASE_URL") |  | ||||||
| 
 |  | ||||||
| 	if err := DropAllTables(PleaseDoubleCheckTheDatabaseURLDontDropProd(connStr)); nil != err { |  | ||||||
| 		t.Fatal(err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										237
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										237
									
								
								main.go
									
									
									
									
									
								
							| @ -1,27 +1,20 @@ | |||||||
| //go:generate go run git.rootprojects.org/root/go-gitver/v2 |  | ||||||
| 
 |  | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"compress/flate" | 	"compress/flate" | ||||||
| 	"flag" | 	"flag" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" |  | ||||||
| 	"log" | 	"log" | ||||||
| 	"math/rand" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"git.example.com/example/goserv/assets" | 	"git.coolaj86.com/coolaj86/goserv/assets" | ||||||
| 	"git.example.com/example/goserv/internal/api" | 	"git.coolaj86.com/coolaj86/goserv/internal/db" | ||||||
| 	"git.example.com/example/goserv/internal/db" |  | ||||||
| 	"git.rootprojects.org/root/keypairs" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-chi/chi" | 	"github.com/go-chi/chi" | ||||||
| 	"github.com/go-chi/chi/middleware" | 	"github.com/go-chi/chi/middleware" | ||||||
| 
 |  | ||||||
| 	_ "github.com/joho/godotenv/autoload" | 	_ "github.com/joho/godotenv/autoload" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -52,9 +45,6 @@ type runOptions struct { | |||||||
| 	trustProxy bool | 	trustProxy bool | ||||||
| 	compress   bool | 	compress   bool | ||||||
| 	static     string | 	static     string | ||||||
| 	pub        string |  | ||||||
| 	oidcWL     string |  | ||||||
| 	demo       bool |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var runFlags *flag.FlagSet | var runFlags *flag.FlagSet | ||||||
| @ -63,32 +53,17 @@ var initFlags *flag.FlagSet | |||||||
| var dbURL string | var dbURL string | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	// chosen by fair dice roll. |  | ||||||
| 	// guaranteed to be random. |  | ||||||
| 	rand.Seed(4) |  | ||||||
| 
 |  | ||||||
| 	// j/k |  | ||||||
| 	rand.Seed(time.Now().UnixNano()) |  | ||||||
| 	initFlags = flag.NewFlagSet("init", flag.ExitOnError) |  | ||||||
| 	var conftodo bool |  | ||||||
| 	var confdomain string |  | ||||||
| 	initFlags.BoolVar(&conftodo, "todo", false, "TODO init should copy out nice templated config files") |  | ||||||
| 	initFlags.StringVar(&confdomain, "base-url", "https://example.com", "TODO the domain to use for templated scripts") |  | ||||||
| 
 |  | ||||||
| 	runOpts = runOptions{} | 	runOpts = runOptions{} | ||||||
| 	runFlags = flag.NewFlagSet("run", flag.ExitOnError) | 	runFlags = flag.NewFlagSet("run", flag.ExitOnError) | ||||||
| 	runFlags.StringVar( | 	runFlags.StringVar( | ||||||
| 		&runOpts.listen, "listen", "", | 		&runOpts.listen, "listen", "", | ||||||
| 		"the address and port on which to listen (default \""+defaultAddr+"\")") | 		"the address and port on which to listen (default \""+defaultAddr+"\")") | ||||||
| 	runFlags.BoolVar(&runOpts.trustProxy, "trust-proxy", false, "trust X-Forwarded-* headers") | 	runFlags.BoolVar(&runOpts.trustProxy, "trust-proxy", false, "trust X-Forwarded-For header") | ||||||
| 	runFlags.BoolVar(&runOpts.compress, "compress", true, "enable compression for text,html,js,css,etc") | 	runFlags.BoolVar(&runOpts.compress, "compress", true, "enable compression for text,html,js,css,etc") | ||||||
| 	runFlags.StringVar(&runOpts.static, "serve-path", "", "path to serve, falls back to built-in web app") | 	runFlags.StringVar(&runOpts.static, "serve-path", "", "path to serve, falls back to built-in web app") | ||||||
| 	runFlags.StringVar(&runOpts.pub, "public-key", "", "path to public key, or key string - RSA or ECDSA, JWK (JSON) or PEM") | 	runFlags.StringVar( | ||||||
| 	runFlags.StringVar(&runOpts.oidcWL, "oidc-whitelist", "", "list of trusted OIDC issuer URLs (ex: Auth0, Google, PocketID) for SSO") | 		&dbURL, "db-url", "postgres://postgres:postgres@localhost:5432/postgres", | ||||||
| 	runFlags.BoolVar(&runOpts.demo, "demo", false, "demo mode enables unauthenticated 'DELETE /api/public/reset' to reset") | 		"database (postgres) connection url") | ||||||
| 	runFlags.StringVar(&dbURL, "database-url", "", |  | ||||||
| 		"database (postgres) connection url (default postgres://postgres:postgres@localhost:5432/postgres)", |  | ||||||
| 	) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
| @ -131,19 +106,6 @@ func main() { | |||||||
| 				runOpts.listen = defaultAddr | 				runOpts.listen = defaultAddr | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if "" == dbURL { |  | ||||||
| 			dbURL = os.Getenv("DATABASE_URL") |  | ||||||
| 		} |  | ||||||
| 		if "" == dbURL { |  | ||||||
| 			dbURL = "postgres://postgres:postgres@localhost:5432/postgres" |  | ||||||
| 		} |  | ||||||
| 		api.OIDCWhitelist = runOpts.oidcWL |  | ||||||
| 		if "" == api.OIDCWhitelist { |  | ||||||
| 			api.OIDCWhitelist = os.Getenv("OIDC_WHITELIST") |  | ||||||
| 		} |  | ||||||
| 		if "" == runOpts.pub { |  | ||||||
| 			runOpts.pub = os.Getenv("PUBLIC_KEY") |  | ||||||
| 		} |  | ||||||
| 		serve() | 		serve() | ||||||
| 	default: | 	default: | ||||||
| 		usage() | 		usage() | ||||||
| @ -152,6 +114,100 @@ func main() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | var startedAt = time.Now() | ||||||
|  | var defaultMaxBytes int64 = 1 << 20 | ||||||
|  | 
 | ||||||
|  | func serve() { | ||||||
|  | 	initDB(dbURL) | ||||||
|  | 
 | ||||||
|  | 	r := chi.NewRouter() | ||||||
|  | 
 | ||||||
|  | 	// A good base middleware stack | ||||||
|  | 	if runOpts.trustProxy { | ||||||
|  | 		r.Use(middleware.RealIP) | ||||||
|  | 	} | ||||||
|  | 	if runOpts.compress { | ||||||
|  | 		r.Use(middleware.Compress(flate.DefaultCompression)) | ||||||
|  | 	} | ||||||
|  | 	r.Use(middleware.Logger) | ||||||
|  | 	r.Use(middleware.Recoverer) | ||||||
|  | 
 | ||||||
|  | 	r.Route("/api", func(r chi.Router) { | ||||||
|  | 		r.Use(limitResponseSize) | ||||||
|  | 		r.Use(jsonAllTheThings) | ||||||
|  | 
 | ||||||
|  | 		r.Route("/public", func(r chi.Router) { | ||||||
|  | 			r.Get("/status", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 				w.Write([]byte(fmt.Sprintf( | ||||||
|  | 					`{ "success": true, "uptime": %.0f }%s`, | ||||||
|  | 					time.Since(startedAt).Seconds(), | ||||||
|  | 					"\n", | ||||||
|  | 				))) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		r.Route("/user", func(r chi.Router) { | ||||||
|  | 			r.Get("/inspect", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 				w.Write([]byte(fmt.Sprintf( | ||||||
|  | 					`{ "success": false, "error": "not implemented" }%s`, "\n", | ||||||
|  | 				))) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	var staticHandler http.HandlerFunc | ||||||
|  | 	pub := http.FileServer(assets.Assets) | ||||||
|  | 
 | ||||||
|  | 	if len(runOpts.static) > 0 { | ||||||
|  | 		// try the user-provided directory first, then fallback to the built-in | ||||||
|  | 		devFS := http.Dir(runOpts.static) | ||||||
|  | 		dev := http.FileServer(devFS) | ||||||
|  | 		staticHandler = func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			if _, err := devFS.Open(r.URL.Path); nil != err { | ||||||
|  | 				pub.ServeHTTP(w, r) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			dev.ServeHTTP(w, r) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		staticHandler = func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			pub.ServeHTTP(w, r) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r.Get("/*", staticHandler) | ||||||
|  | 
 | ||||||
|  | 	fmt.Println("Listening for http (with reasonable timeouts) on", runOpts.listen) | ||||||
|  | 	srv := &http.Server{ | ||||||
|  | 		Addr:              runOpts.listen, | ||||||
|  | 		Handler:           r, | ||||||
|  | 		ReadHeaderTimeout: 2 * time.Second, | ||||||
|  | 		ReadTimeout:       10 * time.Second, | ||||||
|  | 		WriteTimeout:      20 * time.Second, | ||||||
|  | 		MaxHeaderBytes:    1024 * 1024, // 1MiB | ||||||
|  | 	} | ||||||
|  | 	if err := srv.ListenAndServe(); nil != err { | ||||||
|  | 		fmt.Fprintf(os.Stderr, "%s", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func jsonAllTheThings(next http.Handler) http.Handler { | ||||||
|  | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		// just setting a default, other handlers can change this | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		next.ServeHTTP(w, r) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func limitResponseSize(next http.Handler) http.Handler { | ||||||
|  | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		r.Body = http.MaxBytesReader(w, r.Body, defaultMaxBytes) | ||||||
|  | 		next.ServeHTTP(w, r) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func initDB(connStr string) { | func initDB(connStr string) { | ||||||
| 	// TODO url.Parse | 	// TODO url.Parse | ||||||
| 	if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") { | 	if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") { | ||||||
| @ -169,96 +225,3 @@ func initDB(connStr string) { | |||||||
| 
 | 
 | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func serve() { |  | ||||||
| 	initDB(dbURL) |  | ||||||
| 
 |  | ||||||
| 	r := chi.NewRouter() |  | ||||||
| 
 |  | ||||||
| 	// A good base middleware stack |  | ||||||
| 	if runOpts.trustProxy { |  | ||||||
| 		api.TrustProxy = true |  | ||||||
| 		r.Use(middleware.RealIP) |  | ||||||
| 	} |  | ||||||
| 	if runOpts.compress { |  | ||||||
| 		r.Use(middleware.Compress(flate.DefaultCompression)) |  | ||||||
| 	} |  | ||||||
| 	r.Use(middleware.Logger) |  | ||||||
| 	r.Use(middleware.Recoverer) |  | ||||||
| 
 |  | ||||||
| 	var pub keypairs.PublicKey = nil |  | ||||||
| 	if "" != runOpts.pub { |  | ||||||
| 		var err error |  | ||||||
| 		pub, err = keypairs.ParsePublicKey([]byte(runOpts.pub)) |  | ||||||
| 		if nil != err { |  | ||||||
| 			b, err := ioutil.ReadFile(runOpts.pub) |  | ||||||
| 			if nil != err { |  | ||||||
| 				// ignore |  | ||||||
| 			} else { |  | ||||||
| 				pub, err = keypairs.ParsePublicKey(b) |  | ||||||
| 				if nil != err { |  | ||||||
| 					log.Fatal("could not parse public key:", err) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	_ = api.Init(pub, r) |  | ||||||
| 
 |  | ||||||
| 	var staticHandler http.HandlerFunc |  | ||||||
| 	pubdir := http.FileServer(assets.Assets) |  | ||||||
| 
 |  | ||||||
| 	if len(runOpts.static) > 0 { |  | ||||||
| 		// try the user-provided directory first, then fallback to the built-in |  | ||||||
| 		devFS := http.Dir(runOpts.static) |  | ||||||
| 		dev := http.FileServer(devFS) |  | ||||||
| 		staticHandler = func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			if _, err := devFS.Open(r.URL.Path); nil != err { |  | ||||||
| 				pubdir.ServeHTTP(w, r) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			dev.ServeHTTP(w, r) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		staticHandler = func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			pubdir.ServeHTTP(w, r) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if runOpts.demo { |  | ||||||
| 		if err := db.CanDropAllTables(dbURL); nil != err { |  | ||||||
| 			fmt.Fprintf(os.Stderr, err.Error()) |  | ||||||
| 			os.Exit(1) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		fmt.Println("DANGER: running in demo mode with DELETE /api/public/reset enabled") |  | ||||||
| 		fmt.Fprintf(os.Stderr, "DANGER: running in demo mode with DELETE /api/public/reset enabled\n") |  | ||||||
| 		r.Delete("/api/public/reset", func(w http.ResponseWriter, r *http.Request) { |  | ||||||
| 			if err := db.DropAllTables(db.PleaseDoubleCheckTheDatabaseURLDontDropProd(dbURL)); nil != err { |  | ||||||
| 				w.Write([]byte("error dropping tabels: " + err.Error())) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			api.Reset() |  | ||||||
| 			initDB(dbURL) |  | ||||||
| 			w.Write([]byte("re-initialized\n")) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r.Get("/*", staticHandler) |  | ||||||
| 
 |  | ||||||
| 	fmt.Println("") |  | ||||||
| 	fmt.Println("Listening for http (with reasonable timeouts) on", runOpts.listen) |  | ||||||
| 	srv := &http.Server{ |  | ||||||
| 		Addr:              runOpts.listen, |  | ||||||
| 		Handler:           r, |  | ||||||
| 		ReadHeaderTimeout: 2 * time.Second, |  | ||||||
| 		ReadTimeout:       10 * time.Second, |  | ||||||
| 		WriteTimeout:      20 * time.Second, |  | ||||||
| 		MaxHeaderBytes:    1024 * 1024, // 1MiB |  | ||||||
| 	} |  | ||||||
| 	if err := srv.ListenAndServe(); nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "%s\n", err) |  | ||||||
| 		time.Sleep(100 * time.Millisecond) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,76 +1,82 @@ | |||||||
| <!DOCTYPE htmtl> | <!DOCTYPE htmtl> | ||||||
| <html> | <html> | ||||||
|   <head> |     <head> | ||||||
|     <meta charset="UTF-8" /> |         <meta charset="UTF-8" /> | ||||||
|     <title>Example</title> |         <title>Example</title> | ||||||
|     <link |         <link | ||||||
|       rel="stylesheet" |             rel="stylesheet" | ||||||
|       href="./vendor/css/bootswatch.com/4/materia/bootstrap.min.css" |             href="./vendor/css/bootswatch.com/4/materia/bootstrap.min.css" | ||||||
|     /> |         /> | ||||||
|   </head> |     </head> | ||||||
|   <body> |     <body> | ||||||
|     <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> |         <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> | ||||||
|       <a class="navbar-brand" href="#">Example</a> |             <a class="navbar-brand" href="#">Example</a> | ||||||
|       <button |             <button | ||||||
|         class="navbar-toggler" |                 class="navbar-toggler" | ||||||
|         type="button" |                 type="button" | ||||||
|         data-toggle="collapse" |                 data-toggle="collapse" | ||||||
|         data-target="#navbarColor01" |                 data-target="#navbarColor01" | ||||||
|         aria-controls="navbarColor01" |                 aria-controls="navbarColor01" | ||||||
|         aria-expanded="false" |                 aria-expanded="false" | ||||||
|         aria-label="Toggle navigation" |                 aria-label="Toggle navigation" | ||||||
|       > |  | ||||||
|         <span class="navbar-toggler-icon"></span> |  | ||||||
|       </button> |  | ||||||
| 
 |  | ||||||
|       <div class="collapse navbar-collapse" id="navbarColor01"> |  | ||||||
|         <ul class="navbar-nav mr-auto"> |  | ||||||
|           <li class="nav-item active"> |  | ||||||
|             <a class="nav-link" href="#nav-foobar">Foobar</a> |  | ||||||
|           </li> |  | ||||||
|         </ul> |  | ||||||
|         <form class="js-signin form-inline my-2 my-lg-0"> |  | ||||||
|           <input |  | ||||||
|             class="form-control mr-sm-2" |  | ||||||
|             type="email" |  | ||||||
|             name="email" |  | ||||||
|             placeholder="email" |  | ||||||
|           /> |  | ||||||
|           <button class="btn btn-secondary my-2 my-sm-0" type="submit"> |  | ||||||
|             Sign in |  | ||||||
|           </button> |  | ||||||
|         </form> |  | ||||||
|       </div> |  | ||||||
|     </nav> |  | ||||||
|     <div class="container"> |  | ||||||
|       <div class="row"> |  | ||||||
|         <div class="pocket"></div> |  | ||||||
|       </div> |  | ||||||
|       <div class="row"> |  | ||||||
|         <div class="col-xs-12"> |  | ||||||
|           <div class="card border-primary mb-6"> |  | ||||||
|             <a id="nav-foobar" |  | ||||||
|               ><h3 class="card-header"> |  | ||||||
|                 Server Health |  | ||||||
|                 <form class="js-healthcheck"> |  | ||||||
|                   <button type="submit" class="float-right btn btn-primary"> |  | ||||||
|                     Check |  | ||||||
|                   </button> |  | ||||||
|                 </form> |  | ||||||
|               </h3></a |  | ||||||
|             > |             > | ||||||
|             <div class="js-pre card-body"> |                 <span class="navbar-toggler-icon"></span> | ||||||
|               <h5 class="card-title">Check Server Status</h5> |             </button> | ||||||
|               <div class="card-text"> | 
 | ||||||
|                 <pre><code>curl https://example.com/api/public/ping</code></pre> |             <div class="collapse navbar-collapse" id="navbarColor01"> | ||||||
|                 <pre><code class="js-server-health">-</code></pre> |                 <ul class="navbar-nav mr-auto"> | ||||||
|               </div> |                     <li class="nav-item active"> | ||||||
|  |                         <a class="nav-link" href="#nav-foobar">Foobar</a> | ||||||
|  |                     </li> | ||||||
|  |                 </ul> | ||||||
|  |                 <form class="js-signin form-inline my-2 my-lg-0"> | ||||||
|  |                     <input | ||||||
|  |                         class="form-control mr-sm-2" | ||||||
|  |                         type="email" | ||||||
|  |                         name="email" | ||||||
|  |                         placeholder="email" | ||||||
|  |                     /> | ||||||
|  |                     <button | ||||||
|  |                         class="btn btn-secondary my-2 my-sm-0" | ||||||
|  |                         type="submit" | ||||||
|  |                     > | ||||||
|  |                         Sign in | ||||||
|  |                     </button> | ||||||
|  |                 </form> | ||||||
|  |             </div> | ||||||
|  |         </nav> | ||||||
|  |         <div class="container"> | ||||||
|  |             <div class="row"> | ||||||
|  |                 <div class="pocket"></div> | ||||||
|  |             </div> | ||||||
|  |             <div class="row"> | ||||||
|  |                 <div class="col-xs-12"> | ||||||
|  |                     <div class="card border-primary mb-6"> | ||||||
|  |                         <a id="nav-foobar" | ||||||
|  |                             ><h3 class="card-header"> | ||||||
|  |                                 Server Health | ||||||
|  |                                 <form class="js-healthcheck"> | ||||||
|  |                                     <button | ||||||
|  |                                         type="submit" | ||||||
|  |                                         class="float-right btn btn-primary" | ||||||
|  |                                     > | ||||||
|  |                                         Check | ||||||
|  |                                     </button> | ||||||
|  |                                 </form> | ||||||
|  |                             </h3></a | ||||||
|  |                         > | ||||||
|  |                         <div class="card-body"> | ||||||
|  |                             <h5 class="card-title">Check Server Status</h5> | ||||||
|  |                             <div class="card-text"> | ||||||
|  |                                 <pre><code>curl https://example.com/api/public/status</code></pre> | ||||||
|  |                                 <pre><code class="js-server-health">-</code></pre> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|             </div> |             </div> | ||||||
|           </div> |  | ||||||
|         </div> |         </div> | ||||||
|       </div> |         <script src="https://mock.pocketid.app/pocket/consumer.js"></script> | ||||||
|     </div> |         <script src="./js/app.js"></script> | ||||||
|     <script src="https://mock.pocketid.app/pocket/consumer.js"></script> |     </body> | ||||||
|     <script src="./js/app.js"></script> |  | ||||||
|   </body> |  | ||||||
| </html> | </html> | ||||||
|  | |||||||
							
								
								
									
										134
									
								
								public/js/app.js
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								public/js/app.js
									
									
									
									
									
								
							| @ -1,111 +1,49 @@ | |||||||
| (function () { | (function () { | ||||||
|   "use strict"; |     "use strict"; | ||||||
| 
 | 
 | ||||||
|   // AJQuery
 |     // AJQuery
 | ||||||
|   function $(sel, el) { |     function $(sel, el) { | ||||||
|     if (!el) { |         if (!el) { | ||||||
|       el = document; |             el = document; | ||||||
|  |         } | ||||||
|  |         return el.querySelector(sel); | ||||||
|     } |     } | ||||||
|     return el.querySelector(sel); |     function $$(sel, el) { | ||||||
|   } |         if (!el) { | ||||||
|   function $$(sel, el) { |             el = document; | ||||||
|     if (!el) { |         } | ||||||
|       el = document; |         return el.querySelectorAll(sel); | ||||||
|     } |     } | ||||||
|     return el.querySelectorAll(sel); |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   function displayToken(token) { |     function displayToken(token) { | ||||||
|     $$(".js-token").forEach(function (el) { |         $$(".js-token").forEach(function (el) { | ||||||
|       el.innerText = token; |             el.innerText = token; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Pocket.onToken(function (token) { | ||||||
|  |         // TODO Pocket v1.0 will make this obsolete
 | ||||||
|  |         localStorage.setItem("pocket-token", token); | ||||||
|  |         displayToken(); | ||||||
|     }); |     }); | ||||||
|   } |     displayToken(localStorage.getItem("pocket-token")); | ||||||
| 
 | 
 | ||||||
|   Pocket.onToken(function (token) { |     // requires div with class 'pocket'
 | ||||||
|     // TODO Pocket v1.0 will make this obsolete
 |     $("form.js-signin").addEventListener("submit", function (ev) { | ||||||
|     localStorage.setItem("pocket-token", token); |         ev.preventDefault(); | ||||||
|     displayToken(); |         ev.stopPropagation(); | ||||||
|   }); |  | ||||||
|   displayToken(localStorage.getItem("pocket-token")); |  | ||||||
| 
 | 
 | ||||||
|   // requires div with class 'pocket'
 |         var email = $("[name=email]").value; | ||||||
|   $("form.js-signin").addEventListener("submit", function (ev) { |         Pocket.openSignin(ev, { email: email }); | ||||||
|     ev.preventDefault(); |  | ||||||
|     ev.stopPropagation(); |  | ||||||
| 
 |  | ||||||
|     var email = $("[name=email]").value; |  | ||||||
|     Pocket.openSignin(ev, { email: email }); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   $("form.js-healthcheck").addEventListener("submit", function (ev) { |  | ||||||
|     ev.preventDefault(); |  | ||||||
|     ev.stopPropagation(); |  | ||||||
| 
 |  | ||||||
|     window.fetch("/api/public/ping").then(async function (resp) { |  | ||||||
|       var res = await resp.json(); |  | ||||||
|       $(".js-server-health").innerText = JSON.stringify(res, null, 2); |  | ||||||
|     }); |     }); | ||||||
|   }); |  | ||||||
|   ` |  | ||||||
|     # Demo Mode Only |  | ||||||
|     DELETE /public/reset                                    Drop database and re-initialize |  | ||||||
| 
 | 
 | ||||||
|     # Public |     $("form.js-healthcheck").addEventListener("submit", function (ev) { | ||||||
|     GET  /public/ping                                       Health Check |         ev.preventDefault(); | ||||||
|     POST /public/setup                  <= (none)           Bootstrap |         ev.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|     # Admin-only |         window.fetch("/api/public/status").then(async function (resp) { | ||||||
|     GET  /admin/ping                                        (authenticated) Health Check |             var res = await resp.json(); | ||||||
|     GET  /admin/users                      []Users =>       List ALL Users |             $(".js-server-health").innerText = JSON.stringify(res, null, 2); | ||||||
| 
 |         }); | ||||||
|     # User |  | ||||||
|     GET  /user/ping                                         (authenticated) Health Check |  | ||||||
|     GET  /user                          => User             User profile |  | ||||||
|     ` |  | ||||||
|     .trim() |  | ||||||
|     .split(/\n/) |  | ||||||
|     .forEach(function (line) { |  | ||||||
|       line = line.trim(); |  | ||||||
|       if ("#" === line[0] || !line.trim()) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|       line = line.replace(/(<=)?\s*\(none\)\s*(=>)?/g, ""); |  | ||||||
|       var parts = line.split(/\s+/g); |  | ||||||
|       var method = parts[0]; |  | ||||||
|       if ("GET" != method) { |  | ||||||
|         method = "-X " + method + " "; |  | ||||||
|       } else { |  | ||||||
|         method = ""; |  | ||||||
|       } |  | ||||||
|       var pathname = parts[1]; |  | ||||||
|       var auth = pathname.match(/(public|user|admin)/)[1]; |  | ||||||
|       if ("admin" == auth) { |  | ||||||
|         auth = " \\\n    -H 'Authorization: Bearer ADMIN_TOKEN'"; |  | ||||||
|       } else if ("user" == auth) { |  | ||||||
|         auth = " \\\n    -H 'Authorization: Bearer USER_TOKEN'"; |  | ||||||
|       } else { |  | ||||||
|         auth = ""; |  | ||||||
|       } |  | ||||||
|       document.body.querySelector(".js-pre").innerHTML += ( |  | ||||||
|         ` |  | ||||||
|                 <div class="card-text"> |  | ||||||
|                     <pre><code>curl -X POST https://example.com/api</code></pre>
 |  | ||||||
|                     <pre><code class="js-` +
 |  | ||||||
|         pathname.replace(/\//g, "-") + |  | ||||||
|         `">-</code></pre>
 |  | ||||||
|                 </div> |  | ||||||
|             ` |  | ||||||
|       ) |  | ||||||
|         .replace( |  | ||||||
|           /https:\/\/example\.com/g, |  | ||||||
|           location.protocol + "//" + location.host |  | ||||||
|         ) |  | ||||||
|         .replace(/-X POST /g, method) |  | ||||||
|         .replace(/\/api/g, "/api" + pathname + auth); |  | ||||||
|     }); |     }); | ||||||
|   /* |  | ||||||
|     document.body.querySelector(".js-pre").innerHTML = document.body |  | ||||||
|         .querySelector(".js-pre") |  | ||||||
|         .innerHTML |  | ||||||
|         */ |  | ||||||
| })(); | })(); | ||||||
|  | |||||||
| @ -7,7 +7,4 @@ import ( | |||||||
| 	// these are 'go generate' tooling dependencies, not including in the binary | 	// these are 'go generate' tooling dependencies, not including in the binary | ||||||
| 	_ "github.com/shurcooL/vfsgen" | 	_ "github.com/shurcooL/vfsgen" | ||||||
| 	_ "github.com/shurcooL/vfsgen/cmd/vfsgendev" | 	_ "github.com/shurcooL/vfsgen/cmd/vfsgendev" | ||||||
| 	_ "golang.org/x/tools/cmd/stringer" |  | ||||||
| 	_ "git.rootprojects.org/root/keypairs/cmd/keypairs" |  | ||||||
| 	_ "git.rootprojects.org/root/go-gitver/v2" |  | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										312
									
								
								vendor/git.rootprojects.org/root/go-gitver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										312
									
								
								vendor/git.rootprojects.org/root/go-gitver/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,312 +0,0 @@ | |||||||
| Mozilla Public License Version 2.0 |  | ||||||
| 
 |  | ||||||
|    1. Definitions |  | ||||||
| 
 |  | ||||||
| 1.1. "Contributor" means each individual or legal entity that creates, contributes |  | ||||||
| to the creation of, or owns Covered Software. |  | ||||||
| 
 |  | ||||||
| 1.2. "Contributor Version" means the combination of the Contributions of others |  | ||||||
| (if any) used by a Contributor and that particular Contributor's Contribution. |  | ||||||
| 
 |  | ||||||
|       1.3. "Contribution" means Covered Software of a particular Contributor. |  | ||||||
| 
 |  | ||||||
| 1.4. "Covered Software" means Source Code Form to which the initial Contributor |  | ||||||
| has attached the notice in Exhibit A, the Executable Form of such Source Code |  | ||||||
| Form, and Modifications of such Source Code Form, in each case including portions |  | ||||||
| thereof. |  | ||||||
| 
 |  | ||||||
|       1.5. "Incompatible With Secondary Licenses" means |  | ||||||
| 
 |  | ||||||
| (a) that the initial Contributor has attached the notice described in Exhibit |  | ||||||
| B to the Covered Software; or |  | ||||||
| 
 |  | ||||||
| (b) that the Covered Software was made available under the terms of version |  | ||||||
| 1.1 or earlier of the License, but not also under the terms of a Secondary |  | ||||||
| License. |  | ||||||
| 
 |  | ||||||
| 1.6. "Executable Form" means any form of the work other than Source Code Form. |  | ||||||
| 
 |  | ||||||
| 1.7. "Larger Work" means a work that combines Covered Software with other |  | ||||||
| material, in a separate file or files, that is not Covered Software. |  | ||||||
| 
 |  | ||||||
|       1.8. "License" means this document. |  | ||||||
| 
 |  | ||||||
| 1.9. "Licensable" means having the right to grant, to the maximum extent possible, |  | ||||||
| whether at the time of the initial grant or subsequently, any and all of the |  | ||||||
| rights conveyed by this License. |  | ||||||
| 
 |  | ||||||
|       1.10. "Modifications" means any of the following: |  | ||||||
| 
 |  | ||||||
| (a) any file in Source Code Form that results from an addition to, deletion |  | ||||||
| from, or modification of the contents of Covered Software; or |  | ||||||
| 
 |  | ||||||
| (b) any new file in Source Code Form that contains any Covered Software. |  | ||||||
| 
 |  | ||||||
| 1.11. "Patent Claims" of a Contributor means any patent claim(s), including |  | ||||||
| without limitation, method, process, and apparatus claims, in any patent Licensable |  | ||||||
| by such Contributor that would be infringed, but for the grant of the License, |  | ||||||
| by the making, using, selling, offering for sale, having made, import, or |  | ||||||
| transfer of either its Contributions or its Contributor Version. |  | ||||||
| 
 |  | ||||||
| 1.12. "Secondary License" means either the GNU General Public License, Version |  | ||||||
| 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General |  | ||||||
| Public License, Version 3.0, or any later versions of those licenses. |  | ||||||
| 
 |  | ||||||
| 1.13. "Source Code Form" means the form of the work preferred for making modifications. |  | ||||||
| 
 |  | ||||||
| 1.14. "You" (or "Your") means an individual or a legal entity exercising rights |  | ||||||
| under this License. For legal entities, "You" includes any entity that controls, |  | ||||||
| is controlled by, or is under common control with You. For purposes of this |  | ||||||
| definition, "control" means (a) the power, direct or indirect, to cause the |  | ||||||
| direction or management of such entity, whether by contract or otherwise, |  | ||||||
| or (b) ownership of more than fifty percent (50%) of the outstanding shares |  | ||||||
| or beneficial ownership of such entity. |  | ||||||
| 
 |  | ||||||
|    2. License Grants and Conditions |  | ||||||
| 
 |  | ||||||
|       2.1. Grants |  | ||||||
| 
 |  | ||||||
| Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive |  | ||||||
| license: |  | ||||||
| 
 |  | ||||||
| (a) under intellectual property rights (other than patent or trademark) Licensable |  | ||||||
| by such Contributor to use, reproduce, make available, modify, display, perform, |  | ||||||
| distribute, and otherwise exploit its Contributions, either on an unmodified |  | ||||||
| basis, with Modifications, or as part of a Larger Work; and |  | ||||||
| 
 |  | ||||||
| (b) under Patent Claims of such Contributor to make, use, sell, offer for |  | ||||||
| sale, have made, import, and otherwise transfer either its Contributions or |  | ||||||
| its Contributor Version. |  | ||||||
| 
 |  | ||||||
|       2.2. Effective Date |  | ||||||
| 
 |  | ||||||
| The licenses granted in Section 2.1 with respect to any Contribution become |  | ||||||
| effective for each Contribution on the date the Contributor first distributes |  | ||||||
| such Contribution. |  | ||||||
| 
 |  | ||||||
|       2.3. Limitations on Grant Scope |  | ||||||
| 
 |  | ||||||
| The licenses granted in this Section 2 are the only rights granted under this |  | ||||||
| License. No additional rights or licenses will be implied from the distribution |  | ||||||
| or licensing of Covered Software under this License. Notwithstanding Section |  | ||||||
| 2.1(b) above, no patent license is granted by a Contributor: |  | ||||||
| 
 |  | ||||||
| (a) for any code that a Contributor has removed from Covered Software; or |  | ||||||
| 
 |  | ||||||
| (b) for infringements caused by: (i) Your and any other third party's modifications |  | ||||||
| of Covered Software, or (ii) the combination of its Contributions with other |  | ||||||
| software (except as part of its Contributor Version); or |  | ||||||
| 
 |  | ||||||
| (c) under Patent Claims infringed by Covered Software in the absence of its |  | ||||||
| Contributions. |  | ||||||
| 
 |  | ||||||
| This License does not grant any rights in the trademarks, service marks, or |  | ||||||
| logos of any Contributor (except as may be necessary to comply with the notice |  | ||||||
| requirements in Section 3.4). |  | ||||||
| 
 |  | ||||||
|       2.4. Subsequent Licenses |  | ||||||
| 
 |  | ||||||
| No Contributor makes additional grants as a result of Your choice to distribute |  | ||||||
| the Covered Software under a subsequent version of this License (see Section |  | ||||||
| 10.2) or under the terms of a Secondary License (if permitted under the terms |  | ||||||
| of Section 3.3). |  | ||||||
| 
 |  | ||||||
|       2.5. Representation |  | ||||||
| 
 |  | ||||||
| Each Contributor represents that the Contributor believes its Contributions |  | ||||||
| are its original creation(s) or it has sufficient rights to grant the rights |  | ||||||
| to its Contributions conveyed by this License. |  | ||||||
| 
 |  | ||||||
|       2.6. Fair Use |  | ||||||
| 
 |  | ||||||
| This License is not intended to limit any rights You have under applicable |  | ||||||
| copyright doctrines of fair use, fair dealing, or other equivalents. |  | ||||||
| 
 |  | ||||||
|       2.7. Conditions |  | ||||||
| 
 |  | ||||||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in |  | ||||||
| Section 2.1. |  | ||||||
| 
 |  | ||||||
|    3. Responsibilities |  | ||||||
| 
 |  | ||||||
|       3.1. Distribution of Source Form |  | ||||||
| 
 |  | ||||||
| All distribution of Covered Software in Source Code Form, including any Modifications |  | ||||||
| that You create or to which You contribute, must be under the terms of this |  | ||||||
| License. You must inform recipients that the Source Code Form of the Covered |  | ||||||
| Software is governed by the terms of this License, and how they can obtain |  | ||||||
| a copy of this License. You may not attempt to alter or restrict the recipients' |  | ||||||
| rights in the Source Code Form. |  | ||||||
| 
 |  | ||||||
|       3.2. Distribution of Executable Form |  | ||||||
| 
 |  | ||||||
|       If You distribute Covered Software in Executable Form then: |  | ||||||
| 
 |  | ||||||
| (a) such Covered Software must also be made available in Source Code Form, |  | ||||||
| as described in Section 3.1, and You must inform recipients of the Executable |  | ||||||
| Form how they can obtain a copy of such Source Code Form by reasonable means |  | ||||||
| in a timely manner, at a charge no more than the cost of distribution to the |  | ||||||
| recipient; and |  | ||||||
| 
 |  | ||||||
| (b) You may distribute such Executable Form under the terms of this License, |  | ||||||
| or sublicense it under different terms, provided that the license for the |  | ||||||
| Executable Form does not attempt to limit or alter the recipients' rights |  | ||||||
| in the Source Code Form under this License. |  | ||||||
| 
 |  | ||||||
|       3.3. Distribution of a Larger Work |  | ||||||
| 
 |  | ||||||
| You may create and distribute a Larger Work under terms of Your choice, provided |  | ||||||
| that You also comply with the requirements of this License for the Covered |  | ||||||
| Software. If the Larger Work is a combination of Covered Software with a work |  | ||||||
| governed by one or more Secondary Licenses, and the Covered Software is not |  | ||||||
| Incompatible With Secondary Licenses, this License permits You to additionally |  | ||||||
| distribute such Covered Software under the terms of such Secondary License(s), |  | ||||||
| so that the recipient of the Larger Work may, at their option, further distribute |  | ||||||
| the Covered Software under the terms of either this License or such Secondary |  | ||||||
| License(s). |  | ||||||
| 
 |  | ||||||
|       3.4. Notices |  | ||||||
| 
 |  | ||||||
| You may not remove or alter the substance of any license notices (including |  | ||||||
| copyright notices, patent notices, disclaimers of warranty, or limitations |  | ||||||
| of liability) contained within the Source Code Form of the Covered Software, |  | ||||||
| except that You may alter any license notices to the extent required to remedy |  | ||||||
| known factual inaccuracies. |  | ||||||
| 
 |  | ||||||
|       3.5. Application of Additional Terms |  | ||||||
| 
 |  | ||||||
| You may choose to offer, and to charge a fee for, warranty, support, indemnity |  | ||||||
| or liability obligations to one or more recipients of Covered Software. However, |  | ||||||
| You may do so only on Your own behalf, and not on behalf of any Contributor. |  | ||||||
| You must make it absolutely clear that any such warranty, support, indemnity, |  | ||||||
| or liability obligation is offered by You alone, and You hereby agree to indemnify |  | ||||||
| every Contributor for any liability incurred by such Contributor as a result |  | ||||||
| of warranty, support, indemnity or liability terms You offer. You may include |  | ||||||
| additional disclaimers of warranty and limitations of liability specific to |  | ||||||
| any jurisdiction. |  | ||||||
| 
 |  | ||||||
|    4. Inability to Comply Due to Statute or Regulation |  | ||||||
| 
 |  | ||||||
| If it is impossible for You to comply with any of the terms of this License |  | ||||||
| with respect to some or all of the Covered Software due to statute, judicial |  | ||||||
| order, or regulation then You must: (a) comply with the terms of this License |  | ||||||
| to the maximum extent possible; and (b) describe the limitations and the code |  | ||||||
| they affect. Such description must be placed in a text file included with |  | ||||||
| all distributions of the Covered Software under this License. Except to the |  | ||||||
| extent prohibited by statute or regulation, such description must be sufficiently |  | ||||||
| detailed for a recipient of ordinary skill to be able to understand it. |  | ||||||
| 
 |  | ||||||
|    5. Termination |  | ||||||
| 
 |  | ||||||
| 5.1. The rights granted under this License will terminate automatically if |  | ||||||
| You fail to comply with any of its terms. However, if You become compliant, |  | ||||||
| then the rights granted under this License from a particular Contributor are |  | ||||||
| reinstated (a) provisionally, unless and until such Contributor explicitly |  | ||||||
| and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor |  | ||||||
| fails to notify You of the non-compliance by some reasonable means prior to |  | ||||||
| 60 days after You have come back into compliance. Moreover, Your grants from |  | ||||||
| a particular Contributor are reinstated on an ongoing basis if such Contributor |  | ||||||
| notifies You of the non-compliance by some reasonable means, this is the first |  | ||||||
| time You have received notice of non-compliance with this License from such |  | ||||||
| Contributor, and You become compliant prior to 30 days after Your receipt |  | ||||||
| of the notice. |  | ||||||
| 
 |  | ||||||
| 5.2. If You initiate litigation against any entity by asserting a patent infringement |  | ||||||
| claim (excluding declaratory judgment actions, counter-claims, and cross-claims) |  | ||||||
| alleging that a Contributor Version directly or indirectly infringes any patent, |  | ||||||
| then the rights granted to You by any and all Contributors for the Covered |  | ||||||
| Software under Section 2.1 of this License shall terminate. |  | ||||||
| 
 |  | ||||||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end |  | ||||||
| user license agreements (excluding distributors and resellers) which have |  | ||||||
| been validly granted by You or Your distributors under this License prior |  | ||||||
| to termination shall survive termination. |  | ||||||
| 
 |  | ||||||
|    6. Disclaimer of Warranty |  | ||||||
| 
 |  | ||||||
| Covered Software is provided under this License on an "as is" basis, without |  | ||||||
| warranty of any kind, either expressed, implied, or statutory, including, |  | ||||||
| without limitation, warranties that the Covered Software is free of defects, |  | ||||||
| merchantable, fit for a particular purpose or non-infringing. The entire risk |  | ||||||
| as to the quality and performance of the Covered Software is with You. Should |  | ||||||
| any Covered Software prove defective in any respect, You (not any Contributor) |  | ||||||
| assume the cost of any necessary servicing, repair, or correction. This disclaimer |  | ||||||
| of warranty constitutes an essential part of this License. No use of any Covered |  | ||||||
| Software is authorized under this License except under this disclaimer. |  | ||||||
| 
 |  | ||||||
|    7. Limitation of Liability |  | ||||||
| 
 |  | ||||||
| Under no circumstances and under no legal theory, whether tort (including |  | ||||||
| negligence), contract, or otherwise, shall any Contributor, or anyone who |  | ||||||
| distributes Covered Software as permitted above, be liable to You for any |  | ||||||
| direct, indirect, special, incidental, or consequential damages of any character |  | ||||||
| including, without limitation, damages for lost profits, loss of goodwill, |  | ||||||
| work stoppage, computer failure or malfunction, or any and all other commercial |  | ||||||
| damages or losses, even if such party shall have been informed of the possibility |  | ||||||
| of such damages. This limitation of liability shall not apply to liability |  | ||||||
| for death or personal injury resulting from such party's negligence to the |  | ||||||
| extent applicable law prohibits such limitation. Some jurisdictions do not |  | ||||||
| allow the exclusion or limitation of incidental or consequential damages, |  | ||||||
| so this exclusion and limitation may not apply to You. |  | ||||||
| 
 |  | ||||||
|    8. Litigation |  | ||||||
| 
 |  | ||||||
| Any litigation relating to this License may be brought only in the courts |  | ||||||
| of a jurisdiction where the defendant maintains its principal place of business |  | ||||||
| and such litigation shall be governed by laws of that jurisdiction, without |  | ||||||
| reference to its conflict-of-law provisions. Nothing in this Section shall |  | ||||||
| prevent a party's ability to bring cross-claims or counter-claims. |  | ||||||
| 
 |  | ||||||
|    9. Miscellaneous |  | ||||||
| 
 |  | ||||||
| This License represents the complete agreement concerning the subject matter |  | ||||||
| hereof. If any provision of this License is held to be unenforceable, such |  | ||||||
| provision shall be reformed only to the extent necessary to make it enforceable. |  | ||||||
| Any law or regulation which provides that the language of a contract shall |  | ||||||
| be construed against the drafter shall not be used to construe this License |  | ||||||
| against a Contributor. |  | ||||||
| 
 |  | ||||||
|    10. Versions of the License |  | ||||||
| 
 |  | ||||||
|       10.1. New Versions |  | ||||||
| 
 |  | ||||||
| Mozilla Foundation is the license steward. Except as provided in Section 10.3, |  | ||||||
| no one other than the license steward has the right to modify or publish new |  | ||||||
| versions of this License. Each version will be given a distinguishing version |  | ||||||
| number. |  | ||||||
| 
 |  | ||||||
|       10.2. Effect of New Versions |  | ||||||
| 
 |  | ||||||
| You may distribute the Covered Software under the terms of the version of |  | ||||||
| the License under which You originally received the Covered Software, or under |  | ||||||
| the terms of any subsequent version published by the license steward. |  | ||||||
| 
 |  | ||||||
|       10.3. Modified Versions |  | ||||||
| 
 |  | ||||||
| If you create software not governed by this License, and you want to create |  | ||||||
| a new license for such software, you may create and use a modified version |  | ||||||
| of this License if you rename the license and remove any references to the |  | ||||||
| name of the license steward (except to note that such modified license differs |  | ||||||
| from this License). |  | ||||||
| 
 |  | ||||||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses |  | ||||||
| 
 |  | ||||||
| If You choose to distribute Source Code Form that is Incompatible With Secondary |  | ||||||
| Licenses under the terms of this version of the License, the notice described |  | ||||||
| in Exhibit B of this License must be attached. Exhibit A - Source Code Form |  | ||||||
| License Notice |  | ||||||
| 
 |  | ||||||
| This Source Code Form is subject to the terms of the Mozilla Public License, |  | ||||||
| v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain |  | ||||||
| one at http://mozilla.org/MPL/2.0/. |  | ||||||
| 
 |  | ||||||
| If it is not possible or desirable to put the notice in a particular file, |  | ||||||
| then You may include the notice in a location (such as a LICENSE file in a |  | ||||||
| relevant directory) where a recipient would be likely to look for such a notice. |  | ||||||
| 
 |  | ||||||
| You may add additional accurate notices of copyright ownership. |  | ||||||
| 
 |  | ||||||
| Exhibit B - "Incompatible With Secondary Licenses" Notice |  | ||||||
| 
 |  | ||||||
| This Source Code Form is "Incompatible With Secondary Licenses", as defined |  | ||||||
| by the Mozilla Public License, v. 2.0. |  | ||||||
							
								
								
									
										143
									
								
								vendor/git.rootprojects.org/root/go-gitver/gitver/gitver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										143
									
								
								vendor/git.rootprojects.org/root/go-gitver/gitver/gitver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,143 +0,0 @@ | |||||||
| package gitver |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var exactVer *regexp.Regexp |  | ||||||
| var gitVer *regexp.Regexp |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	// exactly vX.Y.Z (go-compatible semver) |  | ||||||
| 	exactVer = regexp.MustCompile(`^v\d+\.\d+\.\d+$`) |  | ||||||
| 
 |  | ||||||
| 	// vX.Y.Z-n-g0000000 git post-release, semver prerelease |  | ||||||
| 	// vX.Y.Z-dirty git post-release, semver prerelease |  | ||||||
| 	gitVer = regexp.MustCompile(`^(v\d+\.\d+)\.(\d+)(-(\d+))?(-(g[0-9a-f]+))?(-(dirty))?`) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Versions struct { |  | ||||||
| 	Timestamp time.Time |  | ||||||
| 	Version   string |  | ||||||
| 	Rev       string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func ExecAndParse() (*Versions, error) { |  | ||||||
| 	desc, err := gitDesc() |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	rev, err := gitRev() |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	ver, err := semVer(desc) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	ts, err := gitTimestamp(desc) |  | ||||||
| 	if nil != err { |  | ||||||
| 		ts = time.Now() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return &Versions{ |  | ||||||
| 		Timestamp: ts, |  | ||||||
| 		Version:   ver, |  | ||||||
| 		Rev:       rev, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func gitDesc() (string, error) { |  | ||||||
| 	args := strings.Split("git describe --tags --dirty --always", " ") |  | ||||||
| 	cmd := exec.Command(args[0], args[1:]...) |  | ||||||
| 	out, err := cmd.CombinedOutput() |  | ||||||
| 	if nil != err { |  | ||||||
| 		// Don't panic, just carry on |  | ||||||
| 		//out = []byte("v0.0.0-0-g0000000") |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	return strings.TrimSpace(string(out)), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func gitRev() (string, error) { |  | ||||||
| 	args := strings.Split("git rev-parse HEAD", " ") |  | ||||||
| 	cmd := exec.Command(args[0], args[1:]...) |  | ||||||
| 	out, err := cmd.CombinedOutput() |  | ||||||
| 	if nil != err { |  | ||||||
| 		return "", fmt.Errorf("\nUnexpected Error\n\n"+ |  | ||||||
| 			"Please open an issue at https://git.rootprojects.org/root/go-gitver/issues/new \n"+ |  | ||||||
| 			"Please include the following:\n\n"+ |  | ||||||
| 			"Command: %s\n"+ |  | ||||||
| 			"Output: %s\n"+ |  | ||||||
| 			"Error: %s\n"+ |  | ||||||
| 			"\nPlease and Thank You.\n\n", strings.Join(args, " "), out, err) |  | ||||||
| 	} |  | ||||||
| 	return strings.TrimSpace(string(out)), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func semVer(desc string) (string, error) { |  | ||||||
| 	if exactVer.MatchString(desc) { |  | ||||||
| 		// v1.0.0 |  | ||||||
| 		return desc, nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !gitVer.MatchString(desc) { |  | ||||||
| 		return "", nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// (v1.0).(0)(-(1))(-(g0000000))(-(dirty)) |  | ||||||
| 	vers := gitVer.FindStringSubmatch(desc) |  | ||||||
| 	patch, err := strconv.Atoi(vers[2]) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return "", fmt.Errorf("\nUnexpected Error\n\n"+ |  | ||||||
| 			"Please open an issue at https://git.rootprojects.org/root/go-gitver/issues/new \n"+ |  | ||||||
| 			"Please include the following:\n\n"+ |  | ||||||
| 			"git description: %s\n"+ |  | ||||||
| 			"RegExp: %#v\n"+ |  | ||||||
| 			"Error: %s\n"+ |  | ||||||
| 			"\nPlease and Thank You.\n\n", desc, gitVer, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// v1.0.1-pre1 |  | ||||||
| 	// v1.0.1-pre1+g0000000 |  | ||||||
| 	// v1.0.1-pre0+dirty |  | ||||||
| 	// v1.0.1-pre0+g0000000-dirty |  | ||||||
| 	if "" == vers[4] { |  | ||||||
| 		vers[4] = "0" |  | ||||||
| 	} |  | ||||||
| 	ver := fmt.Sprintf("%s.%d-pre%s", vers[1], patch+1, vers[4]) |  | ||||||
| 	if "" != vers[6] || "dirty" == vers[8] { |  | ||||||
| 		ver += "+" |  | ||||||
| 		if "" != vers[6] { |  | ||||||
| 			ver += vers[6] |  | ||||||
| 			if "" != vers[8] { |  | ||||||
| 				ver += "-" |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		ver += vers[8] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return ver, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func gitTimestamp(desc string) (time.Time, error) { |  | ||||||
| 	args := []string{ |  | ||||||
| 		"git", |  | ||||||
| 		"show", desc, |  | ||||||
| 		"--format=%cd", |  | ||||||
| 		"--date=format:%Y-%m-%dT%H:%M:%SZ%z", |  | ||||||
| 		"--no-patch", |  | ||||||
| 	} |  | ||||||
| 	cmd := exec.Command(args[0], args[1:]...) |  | ||||||
| 	out, err := cmd.CombinedOutput() |  | ||||||
| 	if nil != err { |  | ||||||
| 		// a dirty desc was probably used |  | ||||||
| 		return time.Time{}, err |  | ||||||
| 	} |  | ||||||
| 	return time.Parse(time.RFC3339, strings.TrimSpace(string(out))) |  | ||||||
| } |  | ||||||
							
								
								
									
										20
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,20 +0,0 @@ | |||||||
| xversion.go |  | ||||||
| zversion.go |  | ||||||
| 
 |  | ||||||
| /go-gitver |  | ||||||
| examples/**.sum |  | ||||||
| 
 |  | ||||||
| # ---> Go |  | ||||||
| # Binaries for programs and plugins |  | ||||||
| *.exe |  | ||||||
| *.exe~ |  | ||||||
| *.dll |  | ||||||
| *.so |  | ||||||
| *.dylib |  | ||||||
| 
 |  | ||||||
| # Test binary, build with `go test -c` |  | ||||||
| *.test |  | ||||||
| 
 |  | ||||||
| # Output of the go coverage tool, specifically when used with LiteIDE |  | ||||||
| *.out |  | ||||||
| 
 |  | ||||||
							
								
								
									
										1
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/.prettierrc
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/.prettierrc
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | |||||||
| {} |  | ||||||
							
								
								
									
										312
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										312
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,312 +0,0 @@ | |||||||
| Mozilla Public License Version 2.0 |  | ||||||
| 
 |  | ||||||
|    1. Definitions |  | ||||||
| 
 |  | ||||||
| 1.1. "Contributor" means each individual or legal entity that creates, contributes |  | ||||||
| to the creation of, or owns Covered Software. |  | ||||||
| 
 |  | ||||||
| 1.2. "Contributor Version" means the combination of the Contributions of others |  | ||||||
| (if any) used by a Contributor and that particular Contributor's Contribution. |  | ||||||
| 
 |  | ||||||
|       1.3. "Contribution" means Covered Software of a particular Contributor. |  | ||||||
| 
 |  | ||||||
| 1.4. "Covered Software" means Source Code Form to which the initial Contributor |  | ||||||
| has attached the notice in Exhibit A, the Executable Form of such Source Code |  | ||||||
| Form, and Modifications of such Source Code Form, in each case including portions |  | ||||||
| thereof. |  | ||||||
| 
 |  | ||||||
|       1.5. "Incompatible With Secondary Licenses" means |  | ||||||
| 
 |  | ||||||
| (a) that the initial Contributor has attached the notice described in Exhibit |  | ||||||
| B to the Covered Software; or |  | ||||||
| 
 |  | ||||||
| (b) that the Covered Software was made available under the terms of version |  | ||||||
| 1.1 or earlier of the License, but not also under the terms of a Secondary |  | ||||||
| License. |  | ||||||
| 
 |  | ||||||
| 1.6. "Executable Form" means any form of the work other than Source Code Form. |  | ||||||
| 
 |  | ||||||
| 1.7. "Larger Work" means a work that combines Covered Software with other |  | ||||||
| material, in a separate file or files, that is not Covered Software. |  | ||||||
| 
 |  | ||||||
|       1.8. "License" means this document. |  | ||||||
| 
 |  | ||||||
| 1.9. "Licensable" means having the right to grant, to the maximum extent possible, |  | ||||||
| whether at the time of the initial grant or subsequently, any and all of the |  | ||||||
| rights conveyed by this License. |  | ||||||
| 
 |  | ||||||
|       1.10. "Modifications" means any of the following: |  | ||||||
| 
 |  | ||||||
| (a) any file in Source Code Form that results from an addition to, deletion |  | ||||||
| from, or modification of the contents of Covered Software; or |  | ||||||
| 
 |  | ||||||
| (b) any new file in Source Code Form that contains any Covered Software. |  | ||||||
| 
 |  | ||||||
| 1.11. "Patent Claims" of a Contributor means any patent claim(s), including |  | ||||||
| without limitation, method, process, and apparatus claims, in any patent Licensable |  | ||||||
| by such Contributor that would be infringed, but for the grant of the License, |  | ||||||
| by the making, using, selling, offering for sale, having made, import, or |  | ||||||
| transfer of either its Contributions or its Contributor Version. |  | ||||||
| 
 |  | ||||||
| 1.12. "Secondary License" means either the GNU General Public License, Version |  | ||||||
| 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General |  | ||||||
| Public License, Version 3.0, or any later versions of those licenses. |  | ||||||
| 
 |  | ||||||
| 1.13. "Source Code Form" means the form of the work preferred for making modifications. |  | ||||||
| 
 |  | ||||||
| 1.14. "You" (or "Your") means an individual or a legal entity exercising rights |  | ||||||
| under this License. For legal entities, "You" includes any entity that controls, |  | ||||||
| is controlled by, or is under common control with You. For purposes of this |  | ||||||
| definition, "control" means (a) the power, direct or indirect, to cause the |  | ||||||
| direction or management of such entity, whether by contract or otherwise, |  | ||||||
| or (b) ownership of more than fifty percent (50%) of the outstanding shares |  | ||||||
| or beneficial ownership of such entity. |  | ||||||
| 
 |  | ||||||
|    2. License Grants and Conditions |  | ||||||
| 
 |  | ||||||
|       2.1. Grants |  | ||||||
| 
 |  | ||||||
| Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive |  | ||||||
| license: |  | ||||||
| 
 |  | ||||||
| (a) under intellectual property rights (other than patent or trademark) Licensable |  | ||||||
| by such Contributor to use, reproduce, make available, modify, display, perform, |  | ||||||
| distribute, and otherwise exploit its Contributions, either on an unmodified |  | ||||||
| basis, with Modifications, or as part of a Larger Work; and |  | ||||||
| 
 |  | ||||||
| (b) under Patent Claims of such Contributor to make, use, sell, offer for |  | ||||||
| sale, have made, import, and otherwise transfer either its Contributions or |  | ||||||
| its Contributor Version. |  | ||||||
| 
 |  | ||||||
|       2.2. Effective Date |  | ||||||
| 
 |  | ||||||
| The licenses granted in Section 2.1 with respect to any Contribution become |  | ||||||
| effective for each Contribution on the date the Contributor first distributes |  | ||||||
| such Contribution. |  | ||||||
| 
 |  | ||||||
|       2.3. Limitations on Grant Scope |  | ||||||
| 
 |  | ||||||
| The licenses granted in this Section 2 are the only rights granted under this |  | ||||||
| License. No additional rights or licenses will be implied from the distribution |  | ||||||
| or licensing of Covered Software under this License. Notwithstanding Section |  | ||||||
| 2.1(b) above, no patent license is granted by a Contributor: |  | ||||||
| 
 |  | ||||||
| (a) for any code that a Contributor has removed from Covered Software; or |  | ||||||
| 
 |  | ||||||
| (b) for infringements caused by: (i) Your and any other third party's modifications |  | ||||||
| of Covered Software, or (ii) the combination of its Contributions with other |  | ||||||
| software (except as part of its Contributor Version); or |  | ||||||
| 
 |  | ||||||
| (c) under Patent Claims infringed by Covered Software in the absence of its |  | ||||||
| Contributions. |  | ||||||
| 
 |  | ||||||
| This License does not grant any rights in the trademarks, service marks, or |  | ||||||
| logos of any Contributor (except as may be necessary to comply with the notice |  | ||||||
| requirements in Section 3.4). |  | ||||||
| 
 |  | ||||||
|       2.4. Subsequent Licenses |  | ||||||
| 
 |  | ||||||
| No Contributor makes additional grants as a result of Your choice to distribute |  | ||||||
| the Covered Software under a subsequent version of this License (see Section |  | ||||||
| 10.2) or under the terms of a Secondary License (if permitted under the terms |  | ||||||
| of Section 3.3). |  | ||||||
| 
 |  | ||||||
|       2.5. Representation |  | ||||||
| 
 |  | ||||||
| Each Contributor represents that the Contributor believes its Contributions |  | ||||||
| are its original creation(s) or it has sufficient rights to grant the rights |  | ||||||
| to its Contributions conveyed by this License. |  | ||||||
| 
 |  | ||||||
|       2.6. Fair Use |  | ||||||
| 
 |  | ||||||
| This License is not intended to limit any rights You have under applicable |  | ||||||
| copyright doctrines of fair use, fair dealing, or other equivalents. |  | ||||||
| 
 |  | ||||||
|       2.7. Conditions |  | ||||||
| 
 |  | ||||||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in |  | ||||||
| Section 2.1. |  | ||||||
| 
 |  | ||||||
|    3. Responsibilities |  | ||||||
| 
 |  | ||||||
|       3.1. Distribution of Source Form |  | ||||||
| 
 |  | ||||||
| All distribution of Covered Software in Source Code Form, including any Modifications |  | ||||||
| that You create or to which You contribute, must be under the terms of this |  | ||||||
| License. You must inform recipients that the Source Code Form of the Covered |  | ||||||
| Software is governed by the terms of this License, and how they can obtain |  | ||||||
| a copy of this License. You may not attempt to alter or restrict the recipients' |  | ||||||
| rights in the Source Code Form. |  | ||||||
| 
 |  | ||||||
|       3.2. Distribution of Executable Form |  | ||||||
| 
 |  | ||||||
|       If You distribute Covered Software in Executable Form then: |  | ||||||
| 
 |  | ||||||
| (a) such Covered Software must also be made available in Source Code Form, |  | ||||||
| as described in Section 3.1, and You must inform recipients of the Executable |  | ||||||
| Form how they can obtain a copy of such Source Code Form by reasonable means |  | ||||||
| in a timely manner, at a charge no more than the cost of distribution to the |  | ||||||
| recipient; and |  | ||||||
| 
 |  | ||||||
| (b) You may distribute such Executable Form under the terms of this License, |  | ||||||
| or sublicense it under different terms, provided that the license for the |  | ||||||
| Executable Form does not attempt to limit or alter the recipients' rights |  | ||||||
| in the Source Code Form under this License. |  | ||||||
| 
 |  | ||||||
|       3.3. Distribution of a Larger Work |  | ||||||
| 
 |  | ||||||
| You may create and distribute a Larger Work under terms of Your choice, provided |  | ||||||
| that You also comply with the requirements of this License for the Covered |  | ||||||
| Software. If the Larger Work is a combination of Covered Software with a work |  | ||||||
| governed by one or more Secondary Licenses, and the Covered Software is not |  | ||||||
| Incompatible With Secondary Licenses, this License permits You to additionally |  | ||||||
| distribute such Covered Software under the terms of such Secondary License(s), |  | ||||||
| so that the recipient of the Larger Work may, at their option, further distribute |  | ||||||
| the Covered Software under the terms of either this License or such Secondary |  | ||||||
| License(s). |  | ||||||
| 
 |  | ||||||
|       3.4. Notices |  | ||||||
| 
 |  | ||||||
| You may not remove or alter the substance of any license notices (including |  | ||||||
| copyright notices, patent notices, disclaimers of warranty, or limitations |  | ||||||
| of liability) contained within the Source Code Form of the Covered Software, |  | ||||||
| except that You may alter any license notices to the extent required to remedy |  | ||||||
| known factual inaccuracies. |  | ||||||
| 
 |  | ||||||
|       3.5. Application of Additional Terms |  | ||||||
| 
 |  | ||||||
| You may choose to offer, and to charge a fee for, warranty, support, indemnity |  | ||||||
| or liability obligations to one or more recipients of Covered Software. However, |  | ||||||
| You may do so only on Your own behalf, and not on behalf of any Contributor. |  | ||||||
| You must make it absolutely clear that any such warranty, support, indemnity, |  | ||||||
| or liability obligation is offered by You alone, and You hereby agree to indemnify |  | ||||||
| every Contributor for any liability incurred by such Contributor as a result |  | ||||||
| of warranty, support, indemnity or liability terms You offer. You may include |  | ||||||
| additional disclaimers of warranty and limitations of liability specific to |  | ||||||
| any jurisdiction. |  | ||||||
| 
 |  | ||||||
|    4. Inability to Comply Due to Statute or Regulation |  | ||||||
| 
 |  | ||||||
| If it is impossible for You to comply with any of the terms of this License |  | ||||||
| with respect to some or all of the Covered Software due to statute, judicial |  | ||||||
| order, or regulation then You must: (a) comply with the terms of this License |  | ||||||
| to the maximum extent possible; and (b) describe the limitations and the code |  | ||||||
| they affect. Such description must be placed in a text file included with |  | ||||||
| all distributions of the Covered Software under this License. Except to the |  | ||||||
| extent prohibited by statute or regulation, such description must be sufficiently |  | ||||||
| detailed for a recipient of ordinary skill to be able to understand it. |  | ||||||
| 
 |  | ||||||
|    5. Termination |  | ||||||
| 
 |  | ||||||
| 5.1. The rights granted under this License will terminate automatically if |  | ||||||
| You fail to comply with any of its terms. However, if You become compliant, |  | ||||||
| then the rights granted under this License from a particular Contributor are |  | ||||||
| reinstated (a) provisionally, unless and until such Contributor explicitly |  | ||||||
| and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor |  | ||||||
| fails to notify You of the non-compliance by some reasonable means prior to |  | ||||||
| 60 days after You have come back into compliance. Moreover, Your grants from |  | ||||||
| a particular Contributor are reinstated on an ongoing basis if such Contributor |  | ||||||
| notifies You of the non-compliance by some reasonable means, this is the first |  | ||||||
| time You have received notice of non-compliance with this License from such |  | ||||||
| Contributor, and You become compliant prior to 30 days after Your receipt |  | ||||||
| of the notice. |  | ||||||
| 
 |  | ||||||
| 5.2. If You initiate litigation against any entity by asserting a patent infringement |  | ||||||
| claim (excluding declaratory judgment actions, counter-claims, and cross-claims) |  | ||||||
| alleging that a Contributor Version directly or indirectly infringes any patent, |  | ||||||
| then the rights granted to You by any and all Contributors for the Covered |  | ||||||
| Software under Section 2.1 of this License shall terminate. |  | ||||||
| 
 |  | ||||||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end |  | ||||||
| user license agreements (excluding distributors and resellers) which have |  | ||||||
| been validly granted by You or Your distributors under this License prior |  | ||||||
| to termination shall survive termination. |  | ||||||
| 
 |  | ||||||
|    6. Disclaimer of Warranty |  | ||||||
| 
 |  | ||||||
| Covered Software is provided under this License on an "as is" basis, without |  | ||||||
| warranty of any kind, either expressed, implied, or statutory, including, |  | ||||||
| without limitation, warranties that the Covered Software is free of defects, |  | ||||||
| merchantable, fit for a particular purpose or non-infringing. The entire risk |  | ||||||
| as to the quality and performance of the Covered Software is with You. Should |  | ||||||
| any Covered Software prove defective in any respect, You (not any Contributor) |  | ||||||
| assume the cost of any necessary servicing, repair, or correction. This disclaimer |  | ||||||
| of warranty constitutes an essential part of this License. No use of any Covered |  | ||||||
| Software is authorized under this License except under this disclaimer. |  | ||||||
| 
 |  | ||||||
|    7. Limitation of Liability |  | ||||||
| 
 |  | ||||||
| Under no circumstances and under no legal theory, whether tort (including |  | ||||||
| negligence), contract, or otherwise, shall any Contributor, or anyone who |  | ||||||
| distributes Covered Software as permitted above, be liable to You for any |  | ||||||
| direct, indirect, special, incidental, or consequential damages of any character |  | ||||||
| including, without limitation, damages for lost profits, loss of goodwill, |  | ||||||
| work stoppage, computer failure or malfunction, or any and all other commercial |  | ||||||
| damages or losses, even if such party shall have been informed of the possibility |  | ||||||
| of such damages. This limitation of liability shall not apply to liability |  | ||||||
| for death or personal injury resulting from such party's negligence to the |  | ||||||
| extent applicable law prohibits such limitation. Some jurisdictions do not |  | ||||||
| allow the exclusion or limitation of incidental or consequential damages, |  | ||||||
| so this exclusion and limitation may not apply to You. |  | ||||||
| 
 |  | ||||||
|    8. Litigation |  | ||||||
| 
 |  | ||||||
| Any litigation relating to this License may be brought only in the courts |  | ||||||
| of a jurisdiction where the defendant maintains its principal place of business |  | ||||||
| and such litigation shall be governed by laws of that jurisdiction, without |  | ||||||
| reference to its conflict-of-law provisions. Nothing in this Section shall |  | ||||||
| prevent a party's ability to bring cross-claims or counter-claims. |  | ||||||
| 
 |  | ||||||
|    9. Miscellaneous |  | ||||||
| 
 |  | ||||||
| This License represents the complete agreement concerning the subject matter |  | ||||||
| hereof. If any provision of this License is held to be unenforceable, such |  | ||||||
| provision shall be reformed only to the extent necessary to make it enforceable. |  | ||||||
| Any law or regulation which provides that the language of a contract shall |  | ||||||
| be construed against the drafter shall not be used to construe this License |  | ||||||
| against a Contributor. |  | ||||||
| 
 |  | ||||||
|    10. Versions of the License |  | ||||||
| 
 |  | ||||||
|       10.1. New Versions |  | ||||||
| 
 |  | ||||||
| Mozilla Foundation is the license steward. Except as provided in Section 10.3, |  | ||||||
| no one other than the license steward has the right to modify or publish new |  | ||||||
| versions of this License. Each version will be given a distinguishing version |  | ||||||
| number. |  | ||||||
| 
 |  | ||||||
|       10.2. Effect of New Versions |  | ||||||
| 
 |  | ||||||
| You may distribute the Covered Software under the terms of the version of |  | ||||||
| the License under which You originally received the Covered Software, or under |  | ||||||
| the terms of any subsequent version published by the license steward. |  | ||||||
| 
 |  | ||||||
|       10.3. Modified Versions |  | ||||||
| 
 |  | ||||||
| If you create software not governed by this License, and you want to create |  | ||||||
| a new license for such software, you may create and use a modified version |  | ||||||
| of this License if you rename the license and remove any references to the |  | ||||||
| name of the license steward (except to note that such modified license differs |  | ||||||
| from this License). |  | ||||||
| 
 |  | ||||||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses |  | ||||||
| 
 |  | ||||||
| If You choose to distribute Source Code Form that is Incompatible With Secondary |  | ||||||
| Licenses under the terms of this version of the License, the notice described |  | ||||||
| in Exhibit B of this License must be attached. Exhibit A - Source Code Form |  | ||||||
| License Notice |  | ||||||
| 
 |  | ||||||
| This Source Code Form is subject to the terms of the Mozilla Public License, |  | ||||||
| v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain |  | ||||||
| one at http://mozilla.org/MPL/2.0/. |  | ||||||
| 
 |  | ||||||
| If it is not possible or desirable to put the notice in a particular file, |  | ||||||
| then You may include the notice in a location (such as a LICENSE file in a |  | ||||||
| relevant directory) where a recipient would be likely to look for such a notice. |  | ||||||
| 
 |  | ||||||
| You may add additional accurate notices of copyright ownership. |  | ||||||
| 
 |  | ||||||
| Exhibit B - "Incompatible With Secondary Licenses" Notice |  | ||||||
| 
 |  | ||||||
| This Source Code Form is "Incompatible With Secondary Licenses", as defined |  | ||||||
| by the Mozilla Public License, v. 2.0. |  | ||||||
							
								
								
									
										256
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										256
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,256 +0,0 @@ | |||||||
| # [Go GitVer](https://git.rootprojects.org/root/go-gitver) |  | ||||||
| 
 |  | ||||||
| Use **git tags** to add (GoRelesear-compatible) [**semver**](https://semver.org/) |  | ||||||
| to your go package in under 150 |  | ||||||
| [lines of code](https://git.rootprojects.org/root/go-gitver/src/branch/master/gitver/gitver.go). |  | ||||||
| 
 |  | ||||||
| ```txt |  | ||||||
| Goals: |  | ||||||
| 
 |  | ||||||
|       1. Use an exact `git tag` version, like v1.0.0, when clean |  | ||||||
|       2. Translate the `git describe` version  (v1.0.0-4-g0000000) |  | ||||||
| 	     to semver (1.0.1-pre4+g0000000) in between releases |  | ||||||
|       3. Note when `dirty` (and have build timestamp) |  | ||||||
| 
 |  | ||||||
|       Fail gracefully when git repo isn't available. |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| # GoDoc |  | ||||||
| 
 |  | ||||||
| See <https://pkg.go.dev/git.rootprojects.org/root/go-gitver>. |  | ||||||
| 
 |  | ||||||
| # How it works |  | ||||||
| 
 |  | ||||||
| 1. You define the fallback version and version printing in `main.go`: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| //go:generate go run git.rootprojects.org/root/go-gitver |  | ||||||
| 
 |  | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	commit  = "0000000" |  | ||||||
| 	version = "0.0.0-pre0+0000000" |  | ||||||
| 	date    = "0000-00-00T00:00:00+0000" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func main() { |  | ||||||
| 	if (len(os.Args) > 1 && "version" === os.Args[1]) { |  | ||||||
| 		fmt.Printf("Foobar v%s (%s) %s\n", version, commit[:7], date) |  | ||||||
| 	} |  | ||||||
| 	// ... |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| 2. You `go generate` or `go run git.rootprojects.org/root/go-gitver` to generate `xversion.go`: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
|     commit  = "0921ed1e" |  | ||||||
|     version = "1.1.2" |  | ||||||
|     date    = "2019-07-01T02:32:58-06:00" |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| # Demo |  | ||||||
| 
 |  | ||||||
| Generate an `xversion.go` file: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go run git.rootprojects.org/root/go-gitver |  | ||||||
| cat xversion.go |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| // Code generated by go generate; DO NOT EDIT. |  | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	commit  = "6dace8255b52e123297a44629bc32c015add310a" |  | ||||||
| 	version = "1.1.4-pre2+g6dace82" |  | ||||||
| 	date    = "2020-07-16T20:48:15-06:00" |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| <small>**Note**: The file is named `xversion.go` by default so that the |  | ||||||
| generated file's `init()` will come later, and thus take priority, over |  | ||||||
| most other files.</small> |  | ||||||
| 
 |  | ||||||
| See `go-gitver`s self-generated version: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go run git.rootprojects.org/root/go-gitver version |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ```txt |  | ||||||
| 6dace8255b52e123297a44629bc32c015add310a |  | ||||||
| v1.1.4-pre2+g6dace82 |  | ||||||
| 2020-07-16T20:48:15-06:00 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| # QuickStart |  | ||||||
| 
 |  | ||||||
| Add this to the top of your main file, so that it runs with `go generate`: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Add a file that imports go-gitver (for versioning) |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| // +build tools |  | ||||||
| 
 |  | ||||||
| package example |  | ||||||
| 
 |  | ||||||
| import _ "git.rootprojects.org/root/go-gitver" |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Change you build instructions to be something like this: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go mod vendor |  | ||||||
| go generate -mod=vendor ./... |  | ||||||
| go build -mod=vendor -o example cmd/example/*.go |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| You don't have to use `-mod=vendor`, but I highly recommend it (just `go mod tidy; go mod vendor` to start). |  | ||||||
| 
 |  | ||||||
| # Options |  | ||||||
| 
 |  | ||||||
| ```txt |  | ||||||
| version           print version and exit |  | ||||||
| --fail            exit with non-zero status code on failure |  | ||||||
| --package <name>  will set the package name |  | ||||||
| --outfile <name>  will replace `xversion.go` with the given file path |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ENVs |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| # Alias for --fail |  | ||||||
| GITVER_FAIL=true |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| For example: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go run -mod=vendor git.rootprojects.org/root/go-gitver version |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| # Usage |  | ||||||
| 
 |  | ||||||
| See `examples/basic` |  | ||||||
| 
 |  | ||||||
| 1. Create a `tools` package in your project |  | ||||||
| 2. Guard it against regular builds with `// +build tools` |  | ||||||
| 3. Include `_ "git.rootprojects.org/root/go-gitver"` in the imports |  | ||||||
| 4. Declare `var commit, version, date string` in your `package main` |  | ||||||
| 5. Include `//go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver` as well |  | ||||||
| 
 |  | ||||||
| `tools/tools.go`: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| // +build tools |  | ||||||
| 
 |  | ||||||
| // This is a dummy package for build tooling |  | ||||||
| package tools |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	_ "git.rootprojects.org/root/go-gitver" |  | ||||||
| ) |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| `main.go`: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| //go:generate go run git.rootprojects.org/root/go-gitver --fail |  | ||||||
| 
 |  | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| import "fmt" |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	commit  = "0000000" |  | ||||||
| 	version = "0.0.0-pre0+0000000" |  | ||||||
| 	date    = "0000-00-00T00:00:00+0000" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func main() { |  | ||||||
|   fmt.Println(commit) |  | ||||||
|   fmt.Println(version) |  | ||||||
|   fmt.Println(date) |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| If you're using `go mod vendor` (which I highly recommend that you do), |  | ||||||
| you'd modify the `go:generate` ever so slightly: |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver --fail |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| The only reason I didn't do that in the example is that I'd be included |  | ||||||
| the repository in itself and that would be... weird. |  | ||||||
| 
 |  | ||||||
| # Why a tools package? |  | ||||||
| 
 |  | ||||||
| > import "git.rootprojects.org/root/go-gitver" is a program, not an importable package |  | ||||||
| 
 |  | ||||||
| Having a tools package with a build tag that you don't use is a nice way to add exact |  | ||||||
| versions of a command package used for tooling to your `go.mod` with `go mod tidy`, |  | ||||||
| without getting the error above. |  | ||||||
| 
 |  | ||||||
| # git: behind the curtain |  | ||||||
| 
 |  | ||||||
| These are the commands that are used under the hood to produce the versions. |  | ||||||
| 
 |  | ||||||
| Shows the git tag + description. Assumes that you're using the semver format `v1.0.0` for your base tags. |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| git describe --tags --dirty --always |  | ||||||
| # v1.0.0 |  | ||||||
| # v1.0.0-1-g0000000 |  | ||||||
| # v1.0.0-dirty |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Show the commit date (when the commit made it into the current tree). |  | ||||||
| Internally we use the current date when the working tree is dirty. |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| git show v1.0.0-1-g0000000 --format=%cd --date=format:%Y-%m-%dT%H:%M:%SZ%z --no-patch |  | ||||||
| # 2010-01-01T20:30:00Z-0600 |  | ||||||
| # fatal: ambiguous argument 'v1.0.0-1-g0000000-dirty': unknown revision or path not in the working tree. |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Shows the most recent commit. |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| git rev-parse HEAD |  | ||||||
| # 0000000000000000000000000000000000000000 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| # Errors |  | ||||||
| 
 |  | ||||||
| ### cannot find package "." |  | ||||||
| 
 |  | ||||||
| ```txt |  | ||||||
| package git.rootprojects.org/root/go-gitver: cannot find package "." in: |  | ||||||
| 	/Users/me/go-example/vendor/git.rootprojects.org/root/go-gitver |  | ||||||
| cmd/example/example.go:1: running "go": exit status 1 |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| You forgot to update deps and re-vendor: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| go mod tidy |  | ||||||
| go mod vendor |  | ||||||
| ``` |  | ||||||
							
								
								
									
										104
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/gitver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										104
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/gitver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,104 +0,0 @@ | |||||||
| //go:generate go run -mod=vendor git.rootprojects.org/root/go-gitver |  | ||||||
| 
 |  | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/format" |  | ||||||
| 	"os" |  | ||||||
| 	"text/template" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.rootprojects.org/root/go-gitver/gitver" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var commit, version, date string |  | ||||||
| var exitCode int |  | ||||||
| var verFile = "xversion.go" |  | ||||||
| 
 |  | ||||||
| func main() { |  | ||||||
| 	pkg := "main" |  | ||||||
| 
 |  | ||||||
| 	args := os.Args[1:] |  | ||||||
| 	for i := range args { |  | ||||||
| 		arg := args[i] |  | ||||||
| 		if "-f" == arg || "--fail" == arg { |  | ||||||
| 			exitCode = 1 |  | ||||||
| 		} else if ("--outfile" == arg || "-o" == arg) && len(args) > i+1 { |  | ||||||
| 			verFile = args[i+1] |  | ||||||
| 			args[i+1] = "" |  | ||||||
| 		} else if "--package" == arg && len(args) > i+1 { |  | ||||||
| 			pkg = args[i+1] |  | ||||||
| 			args[i+1] = "" |  | ||||||
| 		} else if "-V" == arg || "version" == arg || "-version" == arg || "--version" == arg { |  | ||||||
| 			fmt.Println(commit) |  | ||||||
| 			fmt.Println(version) |  | ||||||
| 			fmt.Println(date) |  | ||||||
| 			os.Exit(0) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if "" != os.Getenv("GITVER_FAIL") && "false" != os.Getenv("GITVER_FAIL") { |  | ||||||
| 		exitCode = 1 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v, err := gitver.ExecAndParse() |  | ||||||
| 	if nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to get git version: %s\n", err) |  | ||||||
| 		if exitCode > 0 { |  | ||||||
| 			os.Exit(exitCode) |  | ||||||
| 		} |  | ||||||
| 		v = &gitver.Versions{ |  | ||||||
| 			Timestamp: time.Now(), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Create or overwrite the go file from template |  | ||||||
| 	var buf bytes.Buffer |  | ||||||
| 	if err := versionTpl.Execute(&buf, struct { |  | ||||||
| 		Package   string |  | ||||||
| 		Timestamp string |  | ||||||
| 		Version   string |  | ||||||
| 		Commit    string |  | ||||||
| 	}{ |  | ||||||
| 		Package:   pkg, |  | ||||||
| 		Timestamp: v.Timestamp.Format(time.RFC3339), |  | ||||||
| 		Version:   v.Version, |  | ||||||
| 		Commit:    v.Rev, |  | ||||||
| 	}); nil != err { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Format |  | ||||||
| 	src, err := format.Source(buf.Bytes()) |  | ||||||
| 	if nil != err { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Write to disk (in the Current Working Directory) |  | ||||||
| 	f, err := os.Create(verFile) |  | ||||||
| 	if nil != err { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	if _, err := f.Write(src); nil != err { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	if err := f.Close(); nil != err { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var versionTpl = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT. |  | ||||||
| package {{ .Package }} |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	{{ if .Commit -}} |  | ||||||
| 	commit = "{{ .Commit }}" |  | ||||||
| 	{{ end -}} |  | ||||||
| 	{{ if .Version -}} |  | ||||||
| 	version = "{{ .Version }}" |  | ||||||
| 	{{ end -}} |  | ||||||
| 	date = "{{ .Timestamp }}" |  | ||||||
| } |  | ||||||
| `)) |  | ||||||
							
								
								
									
										3
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | |||||||
| module git.rootprojects.org/root/go-gitver/v2 |  | ||||||
| 
 |  | ||||||
| go 1.12 |  | ||||||
							
								
								
									
										9
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/git.rootprojects.org/root/go-gitver/v2/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,9 +0,0 @@ | |||||||
| package main |  | ||||||
| 
 |  | ||||||
| // use recently generated version info as a fallback |  | ||||||
| // for when git isn't present (i.e. go run <url>) |  | ||||||
| func init() { |  | ||||||
| 	commit = "37c1fd4b5694fd62c9f0d6ad1df47d938accbeec" |  | ||||||
| 	version = "2.0.0-pre1-dirty" |  | ||||||
| 	date = "2020-10-10T16:05:59-06:00" |  | ||||||
| } |  | ||||||
							
								
								
									
										5
									
								
								vendor/git.rootprojects.org/root/keypairs/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/git.rootprojects.org/root/keypairs/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,5 +0,0 @@ | |||||||
| /keypairs |  | ||||||
| /dist/ |  | ||||||
| 
 |  | ||||||
| .DS_Store |  | ||||||
| .*.sw* |  | ||||||
							
								
								
									
										1
									
								
								vendor/git.rootprojects.org/root/keypairs/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/git.rootprojects.org/root/keypairs/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | |||||||
| AJ ONeal <aj@therootcompany.com> (https://therootcompany.com) |  | ||||||
							
								
								
									
										21
									
								
								vendor/git.rootprojects.org/root/keypairs/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/git.rootprojects.org/root/keypairs/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,21 +0,0 @@ | |||||||
| The MIT License |  | ||||||
| 
 |  | ||||||
| Copyright (c) 2018-2019 Big Squid, Inc |  | ||||||
| 
 |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
| 
 |  | ||||||
| The above copyright notice and this permission notice shall be included in all |  | ||||||
| copies or substantial portions of the Software. |  | ||||||
| 
 |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |  | ||||||
| SOFTWARE. |  | ||||||
							
								
								
									
										63
									
								
								vendor/git.rootprojects.org/root/keypairs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/git.rootprojects.org/root/keypairs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,63 +0,0 @@ | |||||||
| # [keypairs](https://git.rootprojects.org/root/keypairs) |  | ||||||
| 
 |  | ||||||
| JSON Web Key (JWK) support and type safety lightly placed over top of Go's `crypto/ecdsa` and `crypto/rsa` |  | ||||||
| 
 |  | ||||||
| Useful for JWT, JOSE, etc. |  | ||||||
| 
 |  | ||||||
| ```go |  | ||||||
| key, err := keypairs.ParsePrivateKey(bytesForJWKOrPEMOrDER) |  | ||||||
| 
 |  | ||||||
| pub, err := keypairs.ParsePublicKey(bytesForJWKOrPEMOrDER) |  | ||||||
| 
 |  | ||||||
| jwk, err := keypairs.MarshalJWKPublicKey(pub, time.Now().Add(2 * time.Day)) |  | ||||||
| 
 |  | ||||||
| kid, err := keypairs.ThumbprintPublicKey(pub) |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| # GoDoc API Documentation |  | ||||||
| 
 |  | ||||||
| See <https://pkg.go.dev/git.rootprojects.org/root/keypairs> |  | ||||||
| 
 |  | ||||||
| # Philosophy |  | ||||||
| 
 |  | ||||||
| Go's standard library is great. |  | ||||||
| 
 |  | ||||||
| Go has _excellent_ crytography support and provides wonderful |  | ||||||
| primitives for dealing with them. |  | ||||||
| 
 |  | ||||||
| I prefer to stay as close to Go's `crypto` package as possible, |  | ||||||
| just adding a light touch for JWT support and type safety. |  | ||||||
| 
 |  | ||||||
| # Type Safety |  | ||||||
| 
 |  | ||||||
| `crypto.PublicKey` is a "marker interface", meaning that it is **not typesafe**! |  | ||||||
| 
 |  | ||||||
| `go-keypairs` defines `type keypairs.PrivateKey interface { Public() crypto.PublicKey }`, |  | ||||||
| which is implemented by `crypto/rsa` and `crypto/ecdsa` |  | ||||||
| (but not `crypto/dsa`, which we really don't care that much about). |  | ||||||
| 
 |  | ||||||
| Go1.15 will add `[PublicKey.Equal(crypto.PublicKey)](https://github.com/golang/go/issues/21704)`, |  | ||||||
| which will make it possible to remove the additional wrapper over `PublicKey` |  | ||||||
| and use an interface instead. |  | ||||||
| 
 |  | ||||||
| Since there are no common methods between `rsa.PublicKey` and `ecdsa.PublicKey`, |  | ||||||
| go-keypairs lightly wraps each to implement `Thumbprint() string` (part of the JOSE/JWK spec). |  | ||||||
| 
 |  | ||||||
| ## JSON Web Key (JWK) as a "codec" |  | ||||||
| 
 |  | ||||||
| Although there are many, many ways that JWKs could be interpreted |  | ||||||
| (possibly why they haven't made it into the standard library), `go-keypairs` |  | ||||||
| follows the basic pattern of `encoding/x509` to `Parse` and `Marshal` |  | ||||||
| only the most basic and most meaningful parts of a key. |  | ||||||
| 
 |  | ||||||
| I highly recommend that you use `Thumbprint()` for `KeyID` you also |  | ||||||
| get the benefit of not losing information when encoding and decoding |  | ||||||
| between the ASN.1, x509, PEM, and JWK formats. |  | ||||||
| 
 |  | ||||||
| # LICENSE |  | ||||||
| 
 |  | ||||||
| Copyright (c) 2020-present AJ ONeal \ |  | ||||||
| Copyright (c) 2018-2019 Big Squid, Inc. |  | ||||||
| 
 |  | ||||||
| This work is licensed under the terms of the MIT license. \ |  | ||||||
| For a copy, see <https://opensource.org/licenses/MIT>. |  | ||||||
							
								
								
									
										19
									
								
								vendor/git.rootprojects.org/root/keypairs/cli_test.sh
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/git.rootprojects.org/root/keypairs/cli_test.sh
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,19 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -u |  | ||||||
| 
 |  | ||||||
| go build -mod=vendor cmd/keypairs/*.go |  | ||||||
| ./keypairs gen > testkey.jwk.json 2> testpub.jwk.json |  | ||||||
| 
 |  | ||||||
| ./keypairs sign --exp 1h ./testkey.jwk.json '{"foo":"bar"}' > testjwt.txt 2> testjws.json |  | ||||||
| 
 |  | ||||||
| echo "" |  | ||||||
| echo "Should pass:" |  | ||||||
| ./keypairs verify ./testpub.jwk.json testjwt.txt > /dev/null |  | ||||||
| ./keypairs verify ./testpub.jwk.json "$(cat testjwt.txt)" > /dev/null |  | ||||||
| ./keypairs verify ./testpub.jwk.json testjws.json > /dev/null |  | ||||||
| ./keypairs verify ./testpub.jwk.json "$(cat testjws.json)" > /dev/null |  | ||||||
| 
 |  | ||||||
| echo "" |  | ||||||
| echo "Should fail:" |  | ||||||
| ./keypairs sign --exp -1m ./testkey.jwk.json '{"bar":"foo"}' > errjwt.txt 2> errjws.json |  | ||||||
| ./keypairs verify ./testpub.jwk.json errjwt.txt > /dev/null |  | ||||||
							
								
								
									
										365
									
								
								vendor/git.rootprojects.org/root/keypairs/cmd/keypairs/keypairs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										365
									
								
								vendor/git.rootprojects.org/root/keypairs/cmd/keypairs/keypairs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,365 +0,0 @@ | |||||||
| package main |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"flag" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"os" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.rootprojects.org/root/keypairs" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	name    = "keypairs" |  | ||||||
| 	version = "0.0.0" |  | ||||||
| 	date    = "0001-01-01T00:00:00Z" |  | ||||||
| 	commit  = "0000000" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func usage() { |  | ||||||
| 	ver() |  | ||||||
| 	fmt.Println("Usage") |  | ||||||
| 	fmt.Printf(" %s <command> [flags] args...\n", name) |  | ||||||
| 	fmt.Println("") |  | ||||||
| 	fmt.Printf("See usage: %s help <command>\n", name) |  | ||||||
| 	fmt.Println("") |  | ||||||
| 	fmt.Println("Commands:") |  | ||||||
| 	fmt.Println("    version") |  | ||||||
| 	fmt.Println("    gen") |  | ||||||
| 	fmt.Println("    sign") |  | ||||||
| 	fmt.Println("    verify") |  | ||||||
| 	fmt.Println("") |  | ||||||
| 	fmt.Println("Examples:") |  | ||||||
| 	fmt.Println("    keypairs gen -o key.jwk.json [--pub <public-key>]") |  | ||||||
| 	fmt.Println("") |  | ||||||
| 	fmt.Println("    keypairs sign --exp 15m key.jwk.json payload.json") |  | ||||||
| 	fmt.Println("    keypairs sign --exp 15m key.jwk.json '{ \"sub\": \"xxxx\" }'") |  | ||||||
| 	fmt.Println("") |  | ||||||
| 	fmt.Println("    keypairs verify ./pub.jwk.json 'xxxx.yyyy.zzzz'") |  | ||||||
| 	// TODO fmt.Println("    keypairs verify --issuer https://example.com '{ \"sub\": \"xxxx\" }'") |  | ||||||
| 	fmt.Println("") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func ver() { |  | ||||||
| 	fmt.Printf("%s v%s %s (%s)\n", name, version, commit[:7], date) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func main() { |  | ||||||
| 	args := os.Args[:] |  | ||||||
| 
 |  | ||||||
| 	if "help" == args[1] { |  | ||||||
| 		// top-level help |  | ||||||
| 		if 2 == len(args) { |  | ||||||
| 			usage() |  | ||||||
| 			os.Exit(0) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		// move help to subcommand argument |  | ||||||
| 		self := args[0] |  | ||||||
| 		args = append([]string{self}, args[2:]...) |  | ||||||
| 		args = append(args, "--help") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch args[1] { |  | ||||||
| 	case "version": |  | ||||||
| 		ver() |  | ||||||
| 		os.Exit(0) |  | ||||||
| 		return |  | ||||||
| 	case "gen": |  | ||||||
| 		gen(args[2:]) |  | ||||||
| 	case "sign": |  | ||||||
| 		sign(args[2:]) |  | ||||||
| 	case "verify": |  | ||||||
| 		verify(args[2:]) |  | ||||||
| 	default: |  | ||||||
| 		usage() |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func gen(args []string) { |  | ||||||
| 	var keyname string |  | ||||||
| 	var pubname string |  | ||||||
| 	flags := flag.NewFlagSet("gen", flag.ExitOnError) |  | ||||||
| 	flags.StringVar(&keyname, "o", "", "private key file (ex: key.jwk.json or key.pem)") |  | ||||||
| 	flags.StringVar(&pubname, "pub", "", "public key file (ex: pub.jwk.json or pub.pem)") |  | ||||||
| 	flags.Parse(args) |  | ||||||
| 
 |  | ||||||
| 	key := keypairs.NewDefaultPrivateKey() |  | ||||||
| 	marshalPriv(key, keyname) |  | ||||||
| 	pub := keypairs.NewPublicKey(key.Public()) |  | ||||||
| 	marshalPub(pub, pubname) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func sign(args []string) { |  | ||||||
| 	var exp time.Duration |  | ||||||
| 	flags := flag.NewFlagSet("sign", flag.ExitOnError) |  | ||||||
| 	flags.DurationVar(&exp, "exp", 0, "duration until token expires (Default 15m)") |  | ||||||
| 	flags.Parse(args) |  | ||||||
| 	if len(flags.Args()) <= 1 { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "Usage: keypairs sign --exp 1h <private PEM or JWK> ./payload.json\n") |  | ||||||
| 		os.Exit(1) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	keyname := flags.Args()[0] |  | ||||||
| 	payload := flags.Args()[1] |  | ||||||
| 
 |  | ||||||
| 	key, err := readKey(keyname) |  | ||||||
| 	if nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "%v\n", err) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if "" == payload { |  | ||||||
| 		// TODO should this be null? I forget |  | ||||||
| 		payload = "{}" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	b, err := ioutil.ReadFile(payload) |  | ||||||
| 	claims := map[string]interface{}{} |  | ||||||
| 	if nil != err { |  | ||||||
| 		var err2 error |  | ||||||
| 		err2 = json.Unmarshal([]byte(payload), &claims) |  | ||||||
| 		if nil != err2 { |  | ||||||
| 			fmt.Fprintf(os.Stderr, |  | ||||||
| 				"could not read payload as file (or parse as string) %q: %s\n", payload, err) |  | ||||||
| 			os.Exit(1) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if 0 == len(claims) { |  | ||||||
| 		var err3 error |  | ||||||
| 		err3 = json.Unmarshal(b, &claims) |  | ||||||
| 		if nil != err3 { |  | ||||||
| 			fmt.Fprintf(os.Stderr, |  | ||||||
| 				"could not parse palyoad from file %q: %s\n", payload, err3) |  | ||||||
| 			os.Exit(1) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if 0 != exp { |  | ||||||
| 		claims["exp"] = exp.Seconds() |  | ||||||
| 	} |  | ||||||
| 	if _, ok := claims["exp"]; !ok { |  | ||||||
| 		claims["exp"] = (15 * time.Minute).Seconds() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jws, err := keypairs.SignClaims(key, nil, claims) |  | ||||||
| 	if nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "could not sign claims: %v\n%#v\n", err, claims) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	b, _ = json.Marshal(&jws) |  | ||||||
| 	fmt.Fprintf(os.Stderr, "%s\n", indentJSON(b)) |  | ||||||
| 	fmt.Fprintf(os.Stdout, "%s\n", keypairs.JWSToJWT(jws)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func verify(args []string) { |  | ||||||
| 	flags := flag.NewFlagSet("verify", flag.ExitOnError) |  | ||||||
| 	flags.Usage = func() { |  | ||||||
| 		fmt.Println("Usage: keypairs verify <public key> <jwt-or-jwt>") |  | ||||||
| 		fmt.Println("") |  | ||||||
| 		fmt.Println("    <public key>: a File or String of an EC or RSA key in JWK or PEM format") |  | ||||||
| 		fmt.Println("    <jwt-or-jws>: a JWT or JWS File or String, if JWS the payload must be Base64") |  | ||||||
| 		fmt.Println("") |  | ||||||
| 	} |  | ||||||
| 	flags.Parse(args) |  | ||||||
| 	if len(flags.Args()) <= 1 { |  | ||||||
| 		flags.Usage() |  | ||||||
| 		os.Exit(1) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	pubname := flags.Args()[0] |  | ||||||
| 	payload := flags.Args()[1] |  | ||||||
| 
 |  | ||||||
| 	pub, err := readPub(pubname) |  | ||||||
| 	if nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "%v\n", err) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jws, err := readJWS(payload) |  | ||||||
| 	if nil != err { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "%v\n", err) |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	b, _ := json.Marshal(&jws) |  | ||||||
| 	fmt.Fprintf(os.Stdout, "%s\n", indentJSON(b)) |  | ||||||
| 
 |  | ||||||
| 	errs := keypairs.VerifyClaims(pub, jws) |  | ||||||
| 	if nil != errs { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "error:\n") |  | ||||||
| 		for _, err := range errs { |  | ||||||
| 			fmt.Fprintf(os.Stderr, "\t%v\n", err) |  | ||||||
| 		} |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	fmt.Fprintf(os.Stderr, "Signature is Valid\n") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func readKey(keyname string) (keypairs.PrivateKey, error) { |  | ||||||
| 	var key keypairs.PrivateKey = nil |  | ||||||
| 
 |  | ||||||
| 	// Read as file |  | ||||||
| 	b, err := ioutil.ReadFile(keyname) |  | ||||||
| 	if nil != err { |  | ||||||
| 		// Tis not a file! Perhaps a string? |  | ||||||
| 		var err2 error |  | ||||||
| 		key, err2 = keypairs.ParsePrivateKey([]byte(keyname)) |  | ||||||
| 		if nil != err2 { |  | ||||||
| 			// Neither a valid string. Blast! |  | ||||||
| 			return nil, fmt.Errorf( |  | ||||||
| 				"could not read private key as file (or parse as string) %q:\n%s", |  | ||||||
| 				keyname, err2, |  | ||||||
| 			) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if nil == key { |  | ||||||
| 		var err3 error |  | ||||||
| 		key, err3 = keypairs.ParsePrivateKey(b) |  | ||||||
| 		if nil != err3 { |  | ||||||
| 			return nil, fmt.Errorf( |  | ||||||
| 				"could not parse private key from file %q:\n%s", |  | ||||||
| 				keyname, err3, |  | ||||||
| 			) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return key, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func readPub(pubname string) (keypairs.PublicKey, error) { |  | ||||||
| 	var pub keypairs.PublicKey = nil |  | ||||||
| 
 |  | ||||||
| 	// Read as file |  | ||||||
| 	b, err := ioutil.ReadFile(pubname) |  | ||||||
| 	if nil != err { |  | ||||||
| 		// No file? Try as string! |  | ||||||
| 		var err2 error |  | ||||||
| 		pub, err2 = keypairs.ParsePublicKey([]byte(pubname)) |  | ||||||
| 		if nil != err2 { |  | ||||||
| 			return nil, fmt.Errorf( |  | ||||||
| 				"could not read public key as file (or parse as string) %q:\n%w", |  | ||||||
| 				pubname, err, |  | ||||||
| 			) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Oh, it was a file. |  | ||||||
| 	if nil == pub { |  | ||||||
| 		var err3 error |  | ||||||
| 		pub, err3 = keypairs.ParsePublicKey(b) |  | ||||||
| 		if nil != err3 { |  | ||||||
| 			return nil, fmt.Errorf( |  | ||||||
| 				"could not parse public key from file %q:\n%w", |  | ||||||
| 				pubname, err3, |  | ||||||
| 			) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return pub, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func readJWS(payload string) (*keypairs.JWS, error) { |  | ||||||
| 	// Is it a file? |  | ||||||
| 	b, err := ioutil.ReadFile(payload) |  | ||||||
| 	if nil != err { |  | ||||||
| 		// Or a JWS or JWS String!? |  | ||||||
| 		b = []byte(payload) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Either way, we have some bytes now |  | ||||||
| 	jws := &keypairs.JWS{} |  | ||||||
| 	jwt := string(b) |  | ||||||
| 	jwsb := []byte(jwt) |  | ||||||
| 	if !strings.Contains(jwt, " \t\n{}[]") { |  | ||||||
| 		jws = keypairs.JWTToJWS(string(b)) |  | ||||||
| 		if nil != jws { |  | ||||||
| 			b, _ = json.Marshal(jws) |  | ||||||
| 			jwsb = (b) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// And now we have a string that may be a JWS |  | ||||||
| 	if err := json.Unmarshal(jwsb, &jws); nil != err { |  | ||||||
| 		// Nope, it's not |  | ||||||
| 		return nil, fmt.Errorf( |  | ||||||
| 			"could not read signed payload from file or string as JWT or JWS %q:\n%w", |  | ||||||
| 			payload, err, |  | ||||||
| 		) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := jws.DecodeComponents(); nil != err { |  | ||||||
| 		// bah! so close! |  | ||||||
| 		return nil, fmt.Errorf( |  | ||||||
| 			"could not decode the JWS Header and Claims components: %w\n%s", |  | ||||||
| 			err, string(jwsb), |  | ||||||
| 		) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return jws, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func marshalPriv(key keypairs.PrivateKey, keyname string) { |  | ||||||
| 	if "" == keyname { |  | ||||||
| 		b := indentJSON(keypairs.MarshalJWKPrivateKey(key)) |  | ||||||
| 
 |  | ||||||
| 		fmt.Fprintf(os.Stdout, string(b)+"\n") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var b []byte |  | ||||||
| 	if strings.HasSuffix(keyname, ".json") { |  | ||||||
| 		b = indentJSON(keypairs.MarshalJWKPrivateKey(key)) |  | ||||||
| 	} else if strings.HasSuffix(keyname, ".pem") { |  | ||||||
| 		b, _ = keypairs.MarshalPEMPrivateKey(key) |  | ||||||
| 	} else if strings.HasSuffix(keyname, ".der") { |  | ||||||
| 		b, _ = keypairs.MarshalDERPrivateKey(key) |  | ||||||
| 	} else { |  | ||||||
| 		fmt.Fprintf(os.Stderr, "private key extension should be .jwk.json, .pem, or .der") |  | ||||||
| 		os.Exit(1) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ioutil.WriteFile(keyname, b, 0600) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func marshalPub(pub keypairs.PublicKey, pubname string) { |  | ||||||
| 	var b []byte |  | ||||||
| 	if "" == pubname { |  | ||||||
| 		b = indentJSON(keypairs.MarshalJWKPublicKey(pub)) |  | ||||||
| 
 |  | ||||||
| 		fmt.Fprintf(os.Stderr, string(b)+"\n") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if strings.HasSuffix(pubname, ".json") { |  | ||||||
| 		b = indentJSON(keypairs.MarshalJWKPublicKey(pub)) |  | ||||||
| 	} else if strings.HasSuffix(pubname, ".pem") { |  | ||||||
| 		b, _ = keypairs.MarshalPEMPublicKey(pub) |  | ||||||
| 	} else if strings.HasSuffix(pubname, ".der") { |  | ||||||
| 		b, _ = keypairs.MarshalDERPublicKey(pub) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ioutil.WriteFile(pubname, b, 0644) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func indentJSON(b []byte) []byte { |  | ||||||
| 	m := map[string]interface{}{} |  | ||||||
| 	_ = json.Unmarshal(b, &m) |  | ||||||
| 	b, _ = json.MarshalIndent(&m, "", "  ") |  | ||||||
| 	return append(b, '\n') |  | ||||||
| } |  | ||||||
							
								
								
									
										40
									
								
								vendor/git.rootprojects.org/root/keypairs/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								vendor/git.rootprojects.org/root/keypairs/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,40 +0,0 @@ | |||||||
| /* |  | ||||||
| Package keypairs complements Go's standard keypair-related packages |  | ||||||
| (encoding/pem, crypto/x509, crypto/rsa, crypto/ecdsa, crypto/elliptic) |  | ||||||
| with JWK encoding support and typesafe PrivateKey and PublicKey interfaces. |  | ||||||
| 
 |  | ||||||
| Basics |  | ||||||
| 
 |  | ||||||
| 	key, err := keypairs.ParsePrivateKey(bytesForJWKOrPEMOrDER) |  | ||||||
| 
 |  | ||||||
| 	pub, err := keypairs.ParsePublicKey(bytesForJWKOrPEMOrDER) |  | ||||||
| 
 |  | ||||||
| 	jwk, err := keypairs.MarshalJWKPublicKey(pub, time.Now().Add(2 * time.Day)) |  | ||||||
| 
 |  | ||||||
| 	kid, err := keypairs.ThumbprintPublicKey(pub) |  | ||||||
| 
 |  | ||||||
| Convenience functions are available which will fetch keys |  | ||||||
| (or retrieve them from cache) via OIDC, .well-known/jwks.json, and direct urls. |  | ||||||
| All keys are cached by Thumbprint, as well as kid(@issuer), if available. |  | ||||||
| 
 |  | ||||||
| 	import "git.rootprojects.org/root/keypairs/keyfetch" |  | ||||||
| 
 |  | ||||||
| 	pubs, err := keyfetch.OIDCJWKs("https://example.com/") |  | ||||||
| 	pubs, err := keyfetch.OIDCJWK(ThumbOrKeyID, "https://example.com/") |  | ||||||
| 
 |  | ||||||
| 	pubs, err := keyfetch.WellKnownJWKs("https://example.com/") |  | ||||||
| 	pubs, err := keyfetch.WellKnownJWK(ThumbOrKeyID, "https://example.com/") |  | ||||||
| 
 |  | ||||||
| 	pubs, err := keyfetch.JWKs("https://example.com/path/to/jwks/") |  | ||||||
| 	pubs, err := keyfetch.JWK(ThumbOrKeyID, "https://example.com/path/to/jwks/") |  | ||||||
| 
 |  | ||||||
| 	// From URL |  | ||||||
| 	pub, err := keyfetch.Fetch("https://example.com/jwk.json") |  | ||||||
| 
 |  | ||||||
| 	// From Cache only |  | ||||||
| 	pub := keyfetch.Get(thumbprint, "https://example.com/jwk.json") |  | ||||||
| 
 |  | ||||||
| A non-caching version with the same capabilities is also available. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| package keypairs |  | ||||||
							
								
								
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/generate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/generate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,69 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"crypto/ecdsa" |  | ||||||
| 	"crypto/elliptic" |  | ||||||
| 	"crypto/rand" |  | ||||||
| 	"crypto/rsa" |  | ||||||
| 	"io" |  | ||||||
| 	mathrand "math/rand" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var randReader io.Reader = rand.Reader |  | ||||||
| var allowMocking = false |  | ||||||
| 
 |  | ||||||
| // KeyOptions are the things that we may need to know about a request to fulfill it properly |  | ||||||
| type keyOptions struct { |  | ||||||
| 	//Key     string `json:"key"` |  | ||||||
| 	KeyType  string `json:"kty"` |  | ||||||
| 	mockSeed int64  //`json:"-"` |  | ||||||
| 	//SeedStr string `json:"seed"` |  | ||||||
| 	//Claims  Object `json:"claims"` |  | ||||||
| 	//Header  Object `json:"header"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (o *keyOptions) nextReader() io.Reader { |  | ||||||
| 	if allowMocking { |  | ||||||
| 		return o.maybeMockReader() |  | ||||||
| 	} |  | ||||||
| 	return randReader |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewDefaultPrivateKey generates a key with reasonable strength. |  | ||||||
| // Today that means a 256-bit equivalent - either RSA 2048 or EC P-256. |  | ||||||
| func NewDefaultPrivateKey() PrivateKey { |  | ||||||
| 	// insecure random is okay here, |  | ||||||
| 	// it's just used for a coin toss |  | ||||||
| 	mathrand.Seed(time.Now().UnixNano()) |  | ||||||
| 	coin := mathrand.Int() |  | ||||||
| 
 |  | ||||||
| 	// the idea here is that we want to make |  | ||||||
| 	// it dead simple to support RSA and EC |  | ||||||
| 	// so it shouldn't matter which is used |  | ||||||
| 	if 0 == coin%2 { |  | ||||||
| 		return newPrivateKey(&keyOptions{ |  | ||||||
| 			KeyType: "RSA", |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 	return newPrivateKey(&keyOptions{ |  | ||||||
| 		KeyType: "EC", |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // newPrivateKey generates a 256-bit entropy RSA or ECDSA private key |  | ||||||
| func newPrivateKey(opts *keyOptions) PrivateKey { |  | ||||||
| 	var privkey PrivateKey |  | ||||||
| 
 |  | ||||||
| 	if "RSA" == opts.KeyType { |  | ||||||
| 		keylen := 2048 |  | ||||||
| 		privkey, _ = rsa.GenerateKey(opts.nextReader(), keylen) |  | ||||||
| 		if allowMocking { |  | ||||||
| 			privkey = maybeDerandomizeMockKey(privkey, keylen, opts) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		// TODO: EC keys may also suffer the same random problems in the future |  | ||||||
| 		privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.nextReader()) |  | ||||||
| 	} |  | ||||||
| 	return privkey |  | ||||||
| } |  | ||||||
							
								
								
									
										3
									
								
								vendor/git.rootprojects.org/root/keypairs/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/git.rootprojects.org/root/keypairs/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | |||||||
| module git.rootprojects.org/root/keypairs |  | ||||||
| 
 |  | ||||||
| go 1.12 |  | ||||||
							
								
								
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/jwk.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/jwk.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,69 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // JWK abstracts EC and RSA keys |  | ||||||
| type JWK interface { |  | ||||||
| 	marshalJWK() ([]byte, error) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ECJWK is the EC variant |  | ||||||
| type ECJWK struct { |  | ||||||
| 	KeyID string   `json:"kid,omitempty"` |  | ||||||
| 	Curve string   `json:"crv"` |  | ||||||
| 	X     string   `json:"x"` |  | ||||||
| 	Y     string   `json:"y"` |  | ||||||
| 	Use   []string `json:"use,omitempty"` |  | ||||||
| 	Seed  string   `json:"_seed,omitempty"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *ECJWK) marshalJWK() ([]byte, error) { |  | ||||||
| 	return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, k.X, k.Y)), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RSAJWK is the RSA variant |  | ||||||
| type RSAJWK struct { |  | ||||||
| 	KeyID string   `json:"kid,omitempty"` |  | ||||||
| 	Exp   string   `json:"e"` |  | ||||||
| 	N     string   `json:"n"` |  | ||||||
| 	Use   []string `json:"use,omitempty"` |  | ||||||
| 	Seed  string   `json:"_seed,omitempty"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *RSAJWK) marshalJWK() ([]byte, error) { |  | ||||||
| 	return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, k.Exp, k.N)), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| // ToPublicJWK exposes only the public parts |  | ||||||
| func ToPublicJWK(pubkey PublicKey) JWK { |  | ||||||
| 	switch k := pubkey.Key().(type) { |  | ||||||
| 	case *ecdsa.PublicKey: |  | ||||||
| 		return ECToPublicJWK(k) |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		return RSAToPublicJWK(k) |  | ||||||
| 	default: |  | ||||||
| 		panic(errors.New("impossible key type")) |  | ||||||
| 		//return nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ECToPublicJWK will output the most minimal version of an EC JWK (no key id, no "use" flag, nada) |  | ||||||
| func ECToPublicJWK(k *ecdsa.PublicKey) *ECJWK { |  | ||||||
| 	return &ECJWK{ |  | ||||||
| 		Curve: k.Curve.Params().Name, |  | ||||||
| 		X:     base64.RawURLEncoding.EncodeToString(k.X.Bytes()), |  | ||||||
| 		Y:     base64.RawURLEncoding.EncodeToString(k.Y.Bytes()), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RSAToPublicJWK will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada) |  | ||||||
| func RSAToPublicJWK(p *rsa.PublicKey) *RSAJWK { |  | ||||||
| 	return &RSAJWK{ |  | ||||||
| 		Exp: base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()), |  | ||||||
| 		N:   base64.RawURLEncoding.EncodeToString(p.N.Bytes()), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| */ |  | ||||||
							
								
								
									
										63
									
								
								vendor/git.rootprojects.org/root/keypairs/jws.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/git.rootprojects.org/root/keypairs/jws.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,63 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // JWS is a parsed JWT, representation as signable/verifiable and human-readable parts |  | ||||||
| type JWS struct { |  | ||||||
| 	Header    Object `json:"header"`    // JSON |  | ||||||
| 	Claims    Object `json:"claims"`    // JSON |  | ||||||
| 	Protected string `json:"protected"` // base64 |  | ||||||
| 	Payload   string `json:"payload"`   // base64 |  | ||||||
| 	Signature string `json:"signature"` // base64 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // JWSToJWT joins JWS parts into a JWT as {ProtectedHeader}.{SerializedPayload}.{Signature}. |  | ||||||
| func JWSToJWT(jwt *JWS) string { |  | ||||||
| 	return fmt.Sprintf( |  | ||||||
| 		"%s.%s.%s", |  | ||||||
| 		jwt.Protected, |  | ||||||
| 		jwt.Payload, |  | ||||||
| 		jwt.Signature, |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // JWTToJWS splits the JWT into its JWS segments |  | ||||||
| func JWTToJWS(jwt string) (jws *JWS) { |  | ||||||
| 	jwt = strings.TrimSpace(jwt) |  | ||||||
| 	parts := strings.Split(jwt, ".") |  | ||||||
| 	if 3 != len(parts) { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return &JWS{ |  | ||||||
| 		Protected: parts[0], |  | ||||||
| 		Payload:   parts[1], |  | ||||||
| 		Signature: parts[2], |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // DecodeComponents decodes JWS Header and Claims |  | ||||||
| func (jws *JWS) DecodeComponents() error { |  | ||||||
| 	protected, err := base64.RawURLEncoding.DecodeString(jws.Protected) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return errors.New("invalid JWS header base64Url encoding") |  | ||||||
| 	} |  | ||||||
| 	if err := json.Unmarshal([]byte(protected), &jws.Header); nil != err { |  | ||||||
| 		return errors.New("invalid JWS header") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	payload, err := base64.RawURLEncoding.DecodeString(jws.Payload) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return errors.New("invalid JWS payload base64Url encoding") |  | ||||||
| 	} |  | ||||||
| 	if err := json.Unmarshal([]byte(payload), &jws.Claims); nil != err { |  | ||||||
| 		return errors.New("invalid JWS claims") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
							
								
								
									
										516
									
								
								vendor/git.rootprojects.org/root/keypairs/keyfetch/fetch.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										516
									
								
								vendor/git.rootprojects.org/root/keypairs/keyfetch/fetch.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,516 +0,0 @@ | |||||||
| // Package keyfetch retrieve and cache PublicKeys |  | ||||||
| // from OIDC (https://example.com/.well-known/openid-configuration) |  | ||||||
| // and Auth0 (https://example.com/.well-known/jwks.json) |  | ||||||
| // JWKs URLs and expires them when `exp` is reached |  | ||||||
| // (or a default expiry if the key does not provide one). |  | ||||||
| // It uses the keypairs package to Unmarshal the JWKs into their |  | ||||||
| // native types (with a very thin shim to provide the type safety |  | ||||||
| // that Go's crypto.PublicKey and crypto.PrivateKey interfaces lack). |  | ||||||
| package keyfetch |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
| 	"net/url" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.rootprojects.org/root/keypairs" |  | ||||||
| 	"git.rootprojects.org/root/keypairs/keyfetch/uncached" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // TODO should be ErrInvalidJWKURL |  | ||||||
| 
 |  | ||||||
| // EInvalidJWKURL means that the url did not provide JWKs |  | ||||||
| var EInvalidJWKURL = errors.New("url does not lead to valid JWKs") |  | ||||||
| 
 |  | ||||||
| // KeyCache is an in-memory key cache |  | ||||||
| var KeyCache = map[string]CachableKey{} |  | ||||||
| 
 |  | ||||||
| // KeyCacheMux is used to guard the in-memory cache |  | ||||||
| var KeyCacheMux = sync.Mutex{} |  | ||||||
| 
 |  | ||||||
| // ErrInsecureDomain means that plain http was used where https was expected |  | ||||||
| var ErrInsecureDomain = errors.New("Whitelists should only allow secure URLs (i.e. https://). To allow unsecured private networking (i.e. Docker) pass PrivateWhitelist as a list of private URLs") |  | ||||||
| 
 |  | ||||||
| // TODO Cacheable key (shouldn't this be private)? |  | ||||||
| 
 |  | ||||||
| // CachableKey represents |  | ||||||
| type CachableKey struct { |  | ||||||
| 	Key    keypairs.PublicKey |  | ||||||
| 	Expiry time.Time |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // maybe TODO use this poor-man's enum to allow kids thumbs to be accepted by the same method? |  | ||||||
| /* |  | ||||||
| type KeyID string |  | ||||||
| 
 |  | ||||||
| func (kid KeyID) ID() string { |  | ||||||
| 	return string(kid) |  | ||||||
| } |  | ||||||
| func (kid KeyID) isID() {} |  | ||||||
| 
 |  | ||||||
| type Thumbprint string |  | ||||||
| 
 |  | ||||||
| func (thumb Thumbprint) ID() string { |  | ||||||
| 	return string(thumb) |  | ||||||
| } |  | ||||||
| func (thumb Thumbprint) isID() {} |  | ||||||
| 
 |  | ||||||
| type ID interface { |  | ||||||
| 	ID() string |  | ||||||
| 	isID() |  | ||||||
| } |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| // StaleTime defines when public keys should be renewed (15 minutes by default) |  | ||||||
| var StaleTime = 15 * time.Minute |  | ||||||
| 
 |  | ||||||
| // DefaultKeyDuration defines how long a key should be considered fresh (48 hours by default) |  | ||||||
| var DefaultKeyDuration = 48 * time.Hour |  | ||||||
| 
 |  | ||||||
| // MinimumKeyDuration defines the minimum time that a key will be cached (1 hour by default) |  | ||||||
| var MinimumKeyDuration = time.Hour |  | ||||||
| 
 |  | ||||||
| // MaximumKeyDuration defines the maximum time that a key will be cached (72 hours by default) |  | ||||||
| var MaximumKeyDuration = 72 * time.Hour |  | ||||||
| 
 |  | ||||||
| // PublicKeysMap is a newtype for a map of keypairs.PublicKey |  | ||||||
| type PublicKeysMap map[string]keypairs.PublicKey |  | ||||||
| 
 |  | ||||||
| // OIDCJWKs fetches baseURL + ".well-known/openid-configuration" and then fetches and returns the Public Keys. |  | ||||||
| func OIDCJWKs(baseURL string) (PublicKeysMap, error) { |  | ||||||
| 	maps, keys, err := uncached.OIDCJWKs(baseURL) |  | ||||||
| 
 |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	cacheKeys(maps, keys, baseURL) |  | ||||||
| 	return keys, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // OIDCJWK fetches baseURL + ".well-known/openid-configuration" and then returns the key matching kid (or thumbprint) |  | ||||||
| func OIDCJWK(kidOrThumb, iss string) (keypairs.PublicKey, error) { |  | ||||||
| 	return immediateOneOrFetch(kidOrThumb, iss, uncached.OIDCJWKs) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // WellKnownJWKs fetches baseURL + ".well-known/jwks.json" and caches and returns the keys |  | ||||||
| func WellKnownJWKs(kidOrThumb, iss string) (PublicKeysMap, error) { |  | ||||||
| 	maps, keys, err := uncached.WellKnownJWKs(iss) |  | ||||||
| 
 |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	cacheKeys(maps, keys, iss) |  | ||||||
| 	return keys, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // WellKnownJWK fetches baseURL + ".well-known/jwks.json" and returns the key matching kid (or thumbprint) |  | ||||||
| func WellKnownJWK(kidOrThumb, iss string) (keypairs.PublicKey, error) { |  | ||||||
| 	return immediateOneOrFetch(kidOrThumb, iss, uncached.WellKnownJWKs) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // JWKs returns a map of keys identified by their thumbprint |  | ||||||
| // (since kid may or may not be present) |  | ||||||
| func JWKs(jwksurl string) (PublicKeysMap, error) { |  | ||||||
| 	maps, keys, err := uncached.JWKs(jwksurl) |  | ||||||
| 
 |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	iss := strings.Replace(jwksurl, ".well-known/jwks.json", "", 1) |  | ||||||
| 	cacheKeys(maps, keys, iss) |  | ||||||
| 	return keys, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // JWK tries to return a key from cache, falling back to the /.well-known/jwks.json of the issuer |  | ||||||
| func JWK(kidOrThumb, iss string) (keypairs.PublicKey, error) { |  | ||||||
| 	return immediateOneOrFetch(kidOrThumb, iss, uncached.JWKs) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // PEM tries to return a key from cache, falling back to the specified PEM url |  | ||||||
| func PEM(url string) (keypairs.PublicKey, error) { |  | ||||||
| 	// url is kid in this case |  | ||||||
| 	return immediateOneOrFetch(url, url, func(string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { |  | ||||||
| 		m, key, err := uncached.PEM(url) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, nil, err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// put in a map, just for caching |  | ||||||
| 		maps := map[string]map[string]string{} |  | ||||||
| 		maps[key.Thumbprint()] = m |  | ||||||
| 		maps[url] = m |  | ||||||
| 
 |  | ||||||
| 		keys := map[string]keypairs.PublicKey{} |  | ||||||
| 		keys[key.Thumbprint()] = key |  | ||||||
| 		keys[url] = key |  | ||||||
| 
 |  | ||||||
| 		return maps, keys, nil |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Fetch returns a key from cache, falling back to an exact url as the "issuer" |  | ||||||
| func Fetch(url string) (keypairs.PublicKey, error) { |  | ||||||
| 	// url is kid in this case |  | ||||||
| 	return immediateOneOrFetch(url, url, func(string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { |  | ||||||
| 		m, key, err := uncached.Fetch(url) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, nil, err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// put in a map, just for caching |  | ||||||
| 		maps := map[string]map[string]string{} |  | ||||||
| 		maps[key.Thumbprint()] = m |  | ||||||
| 
 |  | ||||||
| 		keys := map[string]keypairs.PublicKey{} |  | ||||||
| 		keys[key.Thumbprint()] = key |  | ||||||
| 
 |  | ||||||
| 		return maps, keys, nil |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Get retrieves a key from cache, or returns an error. |  | ||||||
| // The issuer string may be empty if using a thumbprint rather than a kid. |  | ||||||
| func Get(kidOrThumb, iss string) keypairs.PublicKey { |  | ||||||
| 	if pub := get(kidOrThumb, iss); nil != pub { |  | ||||||
| 		return pub.Key |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func get(kidOrThumb, iss string) *CachableKey { |  | ||||||
| 	iss = normalizeIssuer(iss) |  | ||||||
| 	KeyCacheMux.Lock() |  | ||||||
| 	defer KeyCacheMux.Unlock() |  | ||||||
| 
 |  | ||||||
| 	// we're safe to check the cache by kid alone |  | ||||||
| 	// by virtue that we never set it by kid alone |  | ||||||
| 	hit, ok := KeyCache[kidOrThumb] |  | ||||||
| 	if ok { |  | ||||||
| 		if now := time.Now(); hit.Expiry.Sub(now) > 0 { |  | ||||||
| 			// only return non-expired keys |  | ||||||
| 			return &hit |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	id := kidOrThumb + "@" + iss |  | ||||||
| 	hit, ok = KeyCache[id] |  | ||||||
| 	if ok { |  | ||||||
| 		if now := time.Now(); hit.Expiry.Sub(now) > 0 { |  | ||||||
| 			// only return non-expired keys |  | ||||||
| 			return &hit |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func immediateOneOrFetch(kidOrThumb, iss string, fetcher myfetcher) (keypairs.PublicKey, error) { |  | ||||||
| 	now := time.Now() |  | ||||||
| 	key := get(kidOrThumb, iss) |  | ||||||
| 
 |  | ||||||
| 	if nil == key { |  | ||||||
| 		return fetchAndSelect(kidOrThumb, iss, fetcher) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Fetch just a little before the key actually expires |  | ||||||
| 	if key.Expiry.Sub(now) <= StaleTime { |  | ||||||
| 		go fetchAndSelect(kidOrThumb, iss, fetcher) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return key.Key, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type myfetcher func(string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) |  | ||||||
| 
 |  | ||||||
| func fetchAndSelect(id, baseURL string, fetcher myfetcher) (keypairs.PublicKey, error) { |  | ||||||
| 	maps, keys, err := fetcher(baseURL) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	cacheKeys(maps, keys, baseURL) |  | ||||||
| 
 |  | ||||||
| 	for i := range keys { |  | ||||||
| 		key := keys[i] |  | ||||||
| 
 |  | ||||||
| 		if id == key.Thumbprint() { |  | ||||||
| 			return key, nil |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if id == key.KeyID() { |  | ||||||
| 			return key, nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil, fmt.Errorf("Key identified by '%s' was not found at %s", id, baseURL) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func cacheKeys(maps map[string]map[string]string, keys map[string]keypairs.PublicKey, issuer string) { |  | ||||||
| 	for i := range keys { |  | ||||||
| 		key := keys[i] |  | ||||||
| 		m := maps[i] |  | ||||||
| 		iss := issuer |  | ||||||
| 		if "" != m["iss"] { |  | ||||||
| 			iss = m["iss"] |  | ||||||
| 		} |  | ||||||
| 		iss = normalizeIssuer(iss) |  | ||||||
| 		cacheKey(m["kid"], iss, m["exp"], key) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func cacheKey(kid, iss, expstr string, pub keypairs.PublicKey) error { |  | ||||||
| 	var expiry time.Time |  | ||||||
| 	iss = normalizeIssuer(iss) |  | ||||||
| 
 |  | ||||||
| 	exp, _ := strconv.ParseInt(expstr, 10, 64) |  | ||||||
| 	if 0 == exp { |  | ||||||
| 		// use default |  | ||||||
| 		expiry = time.Now().Add(DefaultKeyDuration) |  | ||||||
| 	} else if exp < time.Now().Add(MinimumKeyDuration).Unix() || exp > time.Now().Add(MaximumKeyDuration).Unix() { |  | ||||||
| 		// use at least one hour |  | ||||||
| 		expiry = time.Now().Add(MinimumKeyDuration) |  | ||||||
| 	} else { |  | ||||||
| 		expiry = time.Unix(exp, 0) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	KeyCacheMux.Lock() |  | ||||||
| 	defer KeyCacheMux.Unlock() |  | ||||||
| 	// Put the key in the cache by both kid and thumbprint, and set the expiry |  | ||||||
| 	id := kid + "@" + iss |  | ||||||
| 	KeyCache[id] = CachableKey{ |  | ||||||
| 		Key:    pub, |  | ||||||
| 		Expiry: expiry, |  | ||||||
| 	} |  | ||||||
| 	// Since thumbprints are crypto secure, iss isn't needed |  | ||||||
| 	thumb := pub.Thumbprint() |  | ||||||
| 	KeyCache[thumb] = CachableKey{ |  | ||||||
| 		Key:    pub, |  | ||||||
| 		Expiry: expiry, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func clear() { |  | ||||||
| 	KeyCacheMux.Lock() |  | ||||||
| 	defer KeyCacheMux.Unlock() |  | ||||||
| 	KeyCache = map[string]CachableKey{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func normalizeIssuer(iss string) string { |  | ||||||
| 	return strings.TrimRight(iss, "/") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isTrustedIssuer(iss string, whitelist Whitelist, rs ...*http.Request) bool { |  | ||||||
| 	if "" == iss { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Normalize the http:// and https:// and parse |  | ||||||
| 	iss = strings.TrimRight(iss, "/") + "/" |  | ||||||
| 	if strings.HasPrefix(iss, "http://") { |  | ||||||
| 		// ignore |  | ||||||
| 	} else if strings.HasPrefix(iss, "//") { |  | ||||||
| 		return false // TODO |  | ||||||
| 	} else if !strings.HasPrefix(iss, "https://") { |  | ||||||
| 		iss = "https://" + iss |  | ||||||
| 	} |  | ||||||
| 	issURL, err := url.Parse(iss) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Check that |  | ||||||
| 	// * schemes match (https: == https:) |  | ||||||
| 	// * paths match (/foo/ == /foo/, always with trailing slash added) |  | ||||||
| 	// * hostnames are compatible (a == b or "sub.foo.com".HasSufix(".foo.com")) |  | ||||||
| 	for i := range []*url.URL(whitelist) { |  | ||||||
| 		u := whitelist[i] |  | ||||||
| 
 |  | ||||||
| 		if issURL.Scheme != u.Scheme { |  | ||||||
| 			continue |  | ||||||
| 		} else if u.Path != strings.TrimRight(issURL.Path, "/")+"/" { |  | ||||||
| 			continue |  | ||||||
| 		} else if issURL.Host != u.Host { |  | ||||||
| 			if '.' == u.Host[0] && strings.HasSuffix(issURL.Host, u.Host) { |  | ||||||
| 				return true |  | ||||||
| 			} |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		// All failures have been handled |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Check if implicit issuer is available |  | ||||||
| 	if 0 == len(rs) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return hasImplicitTrust(issURL, rs[0]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // hasImplicitTrust relies on the security of DNS and TLS to determine if the |  | ||||||
| // headers of the request can be trusted as identifying the server itself as |  | ||||||
| // a valid issuer, without additional configuration. |  | ||||||
| // |  | ||||||
| // Helpful for testing, but in the wrong hands could easily lead to a zero-day. |  | ||||||
| func hasImplicitTrust(issURL *url.URL, r *http.Request) bool { |  | ||||||
| 	if nil == r { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Sanity check that, if a load balancer exists, it isn't misconfigured |  | ||||||
| 	proto := r.Header.Get("X-Forwarded-Proto") |  | ||||||
| 	if "" != proto && proto != "https" { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Get the host |  | ||||||
| 	// * If TLS, block Domain Fronting |  | ||||||
| 	// * Otherwise assume trusted proxy |  | ||||||
| 	// * Otherwise assume test environment |  | ||||||
| 	var host string |  | ||||||
| 	if nil != r.TLS { |  | ||||||
| 		// Note that if this were to be implemented for HTTP/2 it would need to |  | ||||||
| 		// check all names on the certificate, not just the one with which the |  | ||||||
| 		// original connection was established. However, not our problem here. |  | ||||||
| 		// See https://serverfault.com/a/908087/93930 |  | ||||||
| 		if r.TLS.ServerName != r.Host { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 		host = r.Host |  | ||||||
| 	} else { |  | ||||||
| 		host = r.Header.Get("X-Forwarded-Host") |  | ||||||
| 		if "" == host { |  | ||||||
| 			host = r.Host |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Same tests as above, adjusted since it can't handle wildcards and, since |  | ||||||
| 	// the path is variable, we make the assumption that a child can trust a |  | ||||||
| 	// parent, but that a parent cannot trust a child. |  | ||||||
| 	if r.Host != issURL.Host { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	if !strings.HasPrefix(strings.TrimRight(r.URL.Path, "/")+"/", issURL.Path) { |  | ||||||
| 		// Ex: Request URL                                   Token Issuer |  | ||||||
| 		// !"https:example.com/johndoe/api/dothing".HasPrefix("https:example.com/") |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Whitelist is a newtype for an array of URLs |  | ||||||
| type Whitelist []*url.URL |  | ||||||
| 
 |  | ||||||
| // NewWhitelist turns an array of URLs (such as https://example.com/) into |  | ||||||
| // a parsed array of *url.URLs that can be used by the IsTrustedIssuer function |  | ||||||
| func NewWhitelist(issuers []string, privateList ...[]string) (Whitelist, error) { |  | ||||||
| 	var err error |  | ||||||
| 
 |  | ||||||
| 	list := []*url.URL{} |  | ||||||
| 	if 0 != len(issuers) { |  | ||||||
| 		insecure := false |  | ||||||
| 		list, err = newWhitelist(list, issuers, insecure) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if 0 != len(privateList) && 0 != len(privateList[0]) { |  | ||||||
| 		insecure := true |  | ||||||
| 		list, err = newWhitelist(list, privateList[0], insecure) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return Whitelist(list), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func newWhitelist(list []*url.URL, issuers []string, insecure bool) (Whitelist, error) { |  | ||||||
| 	for i := range issuers { |  | ||||||
| 		iss := issuers[i] |  | ||||||
| 		if "" == strings.TrimSpace(iss) { |  | ||||||
| 			fmt.Println("[Warning] You have an empty string in your keyfetch whitelist.") |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Should have a valid http or https prefix |  | ||||||
| 		// TODO support custom prefixes (i.e. app://) ? |  | ||||||
| 		if strings.HasPrefix(iss, "http://") { |  | ||||||
| 			if !insecure { |  | ||||||
| 				log.Println("Oops! You have an insecure domain in your whitelist: ", iss) |  | ||||||
| 				return nil, ErrInsecureDomain |  | ||||||
| 			} |  | ||||||
| 		} else if strings.HasPrefix(iss, "//") { |  | ||||||
| 			// TODO |  | ||||||
| 			return nil, errors.New("Rather than prefixing with // to support multiple protocols, add them seperately:" + iss) |  | ||||||
| 		} else if !strings.HasPrefix(iss, "https://") { |  | ||||||
| 			iss = "https://" + iss |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// trailing slash as a boundary character, which may or may not denote a directory |  | ||||||
| 		iss = strings.TrimRight(iss, "/") + "/" |  | ||||||
| 		u, err := url.Parse(iss) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Strip any * prefix, for easier comparison later |  | ||||||
| 		// *.example.com => .example.com |  | ||||||
| 		if strings.HasPrefix(u.Host, "*.") { |  | ||||||
| 			u.Host = u.Host[1:] |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		list = append(list, u) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return list, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
|   IsTrustedIssuer returns true when the `iss` (i.e. from a token) matches one |  | ||||||
|   in the provided whitelist (also matches wildcard domains). |  | ||||||
| 
 |  | ||||||
|   You may explicitly allow insecure http (i.e. for automated testing) by |  | ||||||
|   including http:// Otherwise the scheme in each item of the whitelist should |  | ||||||
| 	include the "https://" prefix. |  | ||||||
| 
 |  | ||||||
|   SECURITY CONSIDERATIONS (Please Read) |  | ||||||
| 
 |  | ||||||
|   You'll notice that *http.Request is optional. It should only be used under these |  | ||||||
|   three circumstances: |  | ||||||
| 
 |  | ||||||
|     1) Something else guarantees http -> https redirection happens before the |  | ||||||
|        connection gets here AND this server directly handles TLS/SSL. |  | ||||||
| 
 |  | ||||||
|     2) If you're using a load balancer or web server, and this doesn't handle |  | ||||||
|        TLS/SSL directly, that server is _explicitly_ configured to protect |  | ||||||
|        against Domain Fronting attacks. As of 2019, most web servers and load |  | ||||||
|        balancers do not protect against that by default. |  | ||||||
| 
 |  | ||||||
|     3) If you only use it to make your automated integration testing more |  | ||||||
|        and it isn't enabled in production. |  | ||||||
| 
 |  | ||||||
|   Otherwise, DO NOT pass in *http.Request as you will introduce a 0-day |  | ||||||
|   vulnerability allowing an attacker to spoof any token issuer of their choice. |  | ||||||
|   The only reason I allowed this in a public library where non-experts would |  | ||||||
|   encounter it is to make testing easier. |  | ||||||
| */ |  | ||||||
| func (w Whitelist) IsTrustedIssuer(iss string, rs ...*http.Request) bool { |  | ||||||
| 	return isTrustedIssuer(iss, w, rs...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // String will generate a space-delimited list of whitelisted URLs |  | ||||||
| func (w Whitelist) String() string { |  | ||||||
| 	s := []string{} |  | ||||||
| 	for i := range w { |  | ||||||
| 		s = append(s, w[i].String()) |  | ||||||
| 	} |  | ||||||
| 	return strings.Join(s, " ") |  | ||||||
| } |  | ||||||
							
								
								
									
										183
									
								
								vendor/git.rootprojects.org/root/keypairs/keyfetch/uncached/fetch.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										183
									
								
								vendor/git.rootprojects.org/root/keypairs/keyfetch/uncached/fetch.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,183 +0,0 @@ | |||||||
| // Package uncached provides uncached versions of go-keypairs/keyfetch |  | ||||||
| package uncached |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"net" |  | ||||||
| 	"net/http" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"git.rootprojects.org/root/keypairs" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // OIDCJWKs gets the OpenID Connect configuration from the baseURL and then calls JWKs with the specified jwks_uri |  | ||||||
| func OIDCJWKs(baseURL string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { |  | ||||||
| 	baseURL = normalizeBaseURL(baseURL) |  | ||||||
| 	oidcConf := struct { |  | ||||||
| 		JWKSURI string `json:"jwks_uri"` |  | ||||||
| 	}{} |  | ||||||
| 
 |  | ||||||
| 	// must come in as https://<domain>/ |  | ||||||
| 	url := baseURL + ".well-known/openid-configuration" |  | ||||||
| 	err := safeFetch(url, func(body io.Reader) error { |  | ||||||
| 		decoder := json.NewDecoder(body) |  | ||||||
| 		decoder.UseNumber() |  | ||||||
| 		return decoder.Decode(&oidcConf) |  | ||||||
| 	}) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return JWKs(oidcConf.JWKSURI) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // WellKnownJWKs calls JWKs with baseURL + /.well-known/jwks.json as constructs the jwks_uri |  | ||||||
| func WellKnownJWKs(baseURL string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { |  | ||||||
| 	baseURL = normalizeBaseURL(baseURL) |  | ||||||
| 	url := baseURL + ".well-known/jwks.json" |  | ||||||
| 
 |  | ||||||
| 	return JWKs(url) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // JWKs fetches and parses a jwks.json (assuming well-known format) |  | ||||||
| func JWKs(jwksurl string) (map[string]map[string]string, map[string]keypairs.PublicKey, error) { |  | ||||||
| 	keys := map[string]keypairs.PublicKey{} |  | ||||||
| 	maps := map[string]map[string]string{} |  | ||||||
| 	resp := struct { |  | ||||||
| 		Keys []map[string]interface{} `json:"keys"` |  | ||||||
| 	}{ |  | ||||||
| 		Keys: make([]map[string]interface{}, 0, 1), |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := safeFetch(jwksurl, func(body io.Reader) error { |  | ||||||
| 		decoder := json.NewDecoder(body) |  | ||||||
| 		decoder.UseNumber() |  | ||||||
| 		return decoder.Decode(&resp) |  | ||||||
| 	}); nil != err { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i := range resp.Keys { |  | ||||||
| 		k := resp.Keys[i] |  | ||||||
| 		m := getStringMap(k) |  | ||||||
| 
 |  | ||||||
| 		key, err := keypairs.NewJWKPublicKey(m) |  | ||||||
| 
 |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, nil, err |  | ||||||
| 		} |  | ||||||
| 		keys[key.Thumbprint()] = key |  | ||||||
| 		maps[key.Thumbprint()] = m |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return maps, keys, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // PEM fetches and parses a PEM (assuming well-known format) |  | ||||||
| func PEM(pemurl string) (map[string]string, keypairs.PublicKey, error) { |  | ||||||
| 	var pub keypairs.PublicKey |  | ||||||
| 	if err := safeFetch(pemurl, func(body io.Reader) error { |  | ||||||
| 		pem, err := ioutil.ReadAll(body) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		pub, err = keypairs.ParsePublicKey(pem) |  | ||||||
| 		return err |  | ||||||
| 	}); nil != err { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jwk := map[string]interface{}{} |  | ||||||
| 	body := bytes.NewBuffer(keypairs.MarshalJWKPublicKey(pub)) |  | ||||||
| 	decoder := json.NewDecoder(body) |  | ||||||
| 	decoder.UseNumber() |  | ||||||
| 	_ = decoder.Decode(&jwk) |  | ||||||
| 
 |  | ||||||
| 	m := getStringMap(jwk) |  | ||||||
| 	m["kid"] = pemurl |  | ||||||
| 
 |  | ||||||
| 	switch p := pub.(type) { |  | ||||||
| 	case *keypairs.ECPublicKey: |  | ||||||
| 		p.KID = pemurl |  | ||||||
| 	case *keypairs.RSAPublicKey: |  | ||||||
| 		p.KID = pemurl |  | ||||||
| 	default: |  | ||||||
| 		return nil, nil, errors.New("impossible key type") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return m, pub, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Fetch retrieves a single JWK (plain, bare jwk) from a URL (off-spec) |  | ||||||
| func Fetch(url string) (map[string]string, keypairs.PublicKey, error) { |  | ||||||
| 	var m map[string]interface{} |  | ||||||
| 	if err := safeFetch(url, func(body io.Reader) error { |  | ||||||
| 		decoder := json.NewDecoder(body) |  | ||||||
| 		decoder.UseNumber() |  | ||||||
| 		return decoder.Decode(&m) |  | ||||||
| 	}); nil != err { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	n := getStringMap(m) |  | ||||||
| 	key, err := keypairs.NewJWKPublicKey(n) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return n, key, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func getStringMap(m map[string]interface{}) map[string]string { |  | ||||||
| 	n := make(map[string]string) |  | ||||||
| 
 |  | ||||||
| 	// TODO get issuer from x5c, if exists |  | ||||||
| 
 |  | ||||||
| 	// convert map[string]interface{} to map[string]string |  | ||||||
| 	for j := range m { |  | ||||||
| 		switch s := m[j].(type) { |  | ||||||
| 		case string: |  | ||||||
| 			n[j] = s |  | ||||||
| 		default: |  | ||||||
| 			// safely ignore |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return n |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type decodeFunc func(io.Reader) error |  | ||||||
| 
 |  | ||||||
| // TODO: also limit the body size |  | ||||||
| func safeFetch(url string, decoder decodeFunc) error { |  | ||||||
| 	var netTransport = &http.Transport{ |  | ||||||
| 		Dial: (&net.Dialer{ |  | ||||||
| 			Timeout: 5 * time.Second, |  | ||||||
| 		}).Dial, |  | ||||||
| 		TLSHandshakeTimeout: 5 * time.Second, |  | ||||||
| 	} |  | ||||||
| 	var client = &http.Client{ |  | ||||||
| 		Timeout:   time.Second * 10, |  | ||||||
| 		Transport: netTransport, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	req, err := http.NewRequest("GET", url, nil) |  | ||||||
| 	req.Header.Set("User-Agent", "go-keypairs/keyfetch") |  | ||||||
| 	req.Header.Set("Accept", "application/json;q=0.9,*/*;q=0.8") |  | ||||||
| 	res, err := client.Do(req) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer res.Body.Close() |  | ||||||
| 
 |  | ||||||
| 	return decoder(res.Body) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func normalizeBaseURL(iss string) string { |  | ||||||
| 	return strings.TrimRight(iss, "/") + "/" |  | ||||||
| } |  | ||||||
							
								
								
									
										645
									
								
								vendor/git.rootprojects.org/root/keypairs/keypairs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										645
									
								
								vendor/git.rootprojects.org/root/keypairs/keypairs.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,645 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"crypto" |  | ||||||
| 	"crypto/dsa" |  | ||||||
| 	"crypto/ecdsa" |  | ||||||
| 	"crypto/elliptic" |  | ||||||
| 	"crypto/rsa" |  | ||||||
| 	"crypto/sha256" |  | ||||||
| 	"crypto/x509" |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"encoding/pem" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"log" |  | ||||||
| 	"math/big" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // ErrInvalidPrivateKey means that the key is not a valid Private Key |  | ||||||
| var ErrInvalidPrivateKey = errors.New("PrivateKey must be of type *rsa.PrivateKey or *ecdsa.PrivateKey") |  | ||||||
| 
 |  | ||||||
| // ErrInvalidPublicKey means that the key is not a valid Public Key |  | ||||||
| var ErrInvalidPublicKey = errors.New("PublicKey must be of type *rsa.PublicKey or *ecdsa.PublicKey") |  | ||||||
| 
 |  | ||||||
| // ErrParsePublicKey means that the bytes cannot be parsed in any known format |  | ||||||
| var ErrParsePublicKey = errors.New("PublicKey bytes could not be parsed as PEM or DER (PKIX/SPKI, PKCS1, or X509 Certificate) or JWK") |  | ||||||
| 
 |  | ||||||
| // ErrParsePrivateKey means that the bytes cannot be parsed in any known format |  | ||||||
| var ErrParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM or DER (PKCS8, SEC1, or PKCS1) or JWK") |  | ||||||
| 
 |  | ||||||
| // ErrParseJWK means that the JWK is valid JSON but not a valid JWK |  | ||||||
| var ErrParseJWK = errors.New("JWK is missing required base64-encoded JSON fields") |  | ||||||
| 
 |  | ||||||
| // ErrInvalidKeyType means that the key is not an acceptable type |  | ||||||
| var ErrInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'") |  | ||||||
| 
 |  | ||||||
| // ErrInvalidCurve means that a non-standard curve was used |  | ||||||
| var ErrInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'") |  | ||||||
| 
 |  | ||||||
| // ErrUnexpectedPublicKey means that a Private Key was expected |  | ||||||
| var ErrUnexpectedPublicKey = errors.New("PrivateKey was given where PublicKey was expected") |  | ||||||
| 
 |  | ||||||
| // ErrUnexpectedPrivateKey means that a Public Key was expected |  | ||||||
| var ErrUnexpectedPrivateKey = errors.New("PublicKey was given where PrivateKey was expected") |  | ||||||
| 
 |  | ||||||
| // ErrDevSwapPrivatePublic means that the developer compiled bad code that swapped public and private keys |  | ||||||
| const ErrDevSwapPrivatePublic = "[Developer Error] You passed either crypto.PrivateKey or crypto.PublicKey where the other was expected." |  | ||||||
| 
 |  | ||||||
| // ErrDevBadKeyType means that the developer compiled bad code that passes the wrong type |  | ||||||
| const ErrDevBadKeyType = "[Developer Error] crypto.PublicKey and crypto.PrivateKey are somewhat deceptive. They're actually empty interfaces that accept any object, even non-crypto objects. You passed an object of type '%T' by mistake." |  | ||||||
| 
 |  | ||||||
| // PrivateKey is a zero-cost typesafe substitue for crypto.PrivateKey |  | ||||||
| type PrivateKey interface { |  | ||||||
| 	Public() crypto.PublicKey |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // PublicKey thinly veils crypto.PublicKey for type safety |  | ||||||
| type PublicKey interface { |  | ||||||
| 	crypto.PublicKey |  | ||||||
| 	Thumbprint() string |  | ||||||
| 	KeyID() string |  | ||||||
| 	Key() crypto.PublicKey |  | ||||||
| 	ExpiresAt() time.Time |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ECPublicKey adds common methods to *ecdsa.PublicKey for type safety |  | ||||||
| type ECPublicKey struct { |  | ||||||
| 	PublicKey *ecdsa.PublicKey // empty interface |  | ||||||
| 	KID       string |  | ||||||
| 	Expiry    time.Time |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RSAPublicKey adds common methods to *rsa.PublicKey for type safety |  | ||||||
| type RSAPublicKey struct { |  | ||||||
| 	PublicKey *rsa.PublicKey // empty interface |  | ||||||
| 	KID       string |  | ||||||
| 	Expiry    time.Time |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Thumbprint returns a JWK thumbprint. See https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk |  | ||||||
| func (p *ECPublicKey) Thumbprint() string { |  | ||||||
| 	return ThumbprintUntypedPublicKey(p.PublicKey) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // KeyID returns the JWK `kid`, which will be the Thumbprint for keys generated with this library |  | ||||||
| func (p *ECPublicKey) KeyID() string { |  | ||||||
| 	return p.KID |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Key returns the PublicKey |  | ||||||
| func (p *ECPublicKey) Key() crypto.PublicKey { |  | ||||||
| 	return p.PublicKey |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ExpireAt sets the time at which this Public Key should be considered invalid |  | ||||||
| func (p *ECPublicKey) ExpireAt(t time.Time) { |  | ||||||
| 	p.Expiry = t |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ExpiresAt gets the time at which this Public Key should be considered invalid |  | ||||||
| func (p *ECPublicKey) ExpiresAt() time.Time { |  | ||||||
| 	return p.Expiry |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Thumbprint returns a JWK thumbprint. See https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk |  | ||||||
| func (p *RSAPublicKey) Thumbprint() string { |  | ||||||
| 	return ThumbprintUntypedPublicKey(p.PublicKey) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // KeyID returns the JWK `kid`, which will be the Thumbprint for keys generated with this library |  | ||||||
| func (p *RSAPublicKey) KeyID() string { |  | ||||||
| 	return p.KID |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Key returns the PublicKey |  | ||||||
| func (p *RSAPublicKey) Key() crypto.PublicKey { |  | ||||||
| 	return p.PublicKey |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ExpireAt sets the time at which this Public Key should be considered invalid |  | ||||||
| func (p *RSAPublicKey) ExpireAt(t time.Time) { |  | ||||||
| 	p.Expiry = t |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ExpiresAt gets the time at which this Public Key should be considered invalid |  | ||||||
| func (p *RSAPublicKey) ExpiresAt() time.Time { |  | ||||||
| 	return p.Expiry |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewPublicKey wraps a crypto.PublicKey to make it typesafe. |  | ||||||
| func NewPublicKey(pub crypto.PublicKey, kid ...string) PublicKey { |  | ||||||
| 	var k PublicKey |  | ||||||
| 	switch p := pub.(type) { |  | ||||||
| 	case *ecdsa.PublicKey: |  | ||||||
| 		eckey := &ECPublicKey{ |  | ||||||
| 			PublicKey: p, |  | ||||||
| 		} |  | ||||||
| 		if 0 != len(kid) { |  | ||||||
| 			eckey.KID = kid[0] |  | ||||||
| 		} else { |  | ||||||
| 			eckey.KID = ThumbprintECPublicKey(p) |  | ||||||
| 		} |  | ||||||
| 		k = eckey |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		rsakey := &RSAPublicKey{ |  | ||||||
| 			PublicKey: p, |  | ||||||
| 		} |  | ||||||
| 		if 0 != len(kid) { |  | ||||||
| 			rsakey.KID = kid[0] |  | ||||||
| 		} else { |  | ||||||
| 			rsakey.KID = ThumbprintRSAPublicKey(p) |  | ||||||
| 		} |  | ||||||
| 		k = rsakey |  | ||||||
| 	case *ecdsa.PrivateKey: |  | ||||||
| 		panic(errors.New(ErrDevSwapPrivatePublic)) |  | ||||||
| 	case *rsa.PrivateKey: |  | ||||||
| 		panic(errors.New(ErrDevSwapPrivatePublic)) |  | ||||||
| 	case *dsa.PublicKey: |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 	case *dsa.PrivateKey: |  | ||||||
| 		panic(ErrInvalidPrivateKey) |  | ||||||
| 	default: |  | ||||||
| 		panic(fmt.Errorf(ErrDevBadKeyType, pub)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return k |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalJWKPublicKey outputs a JWK with its key id (kid) and an optional expiration, |  | ||||||
| // making it suitable for use as an OIDC public key. |  | ||||||
| func MarshalJWKPublicKey(key PublicKey, exp ...time.Time) []byte { |  | ||||||
| 	// thumbprint keys are alphabetically sorted and only include the necessary public parts |  | ||||||
| 	switch k := key.Key().(type) { |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		return MarshalRSAPublicKey(k, exp...) |  | ||||||
| 	case *ecdsa.PublicKey: |  | ||||||
| 		return MarshalECPublicKey(k, exp...) |  | ||||||
| 	case *dsa.PublicKey: |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 	default: |  | ||||||
| 		// this is unreachable because we know the types that we pass in |  | ||||||
| 		log.Printf("keytype: %t, %+v\n", key, key) |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ThumbprintPublicKey returns the SHA256 RFC-spec JWK thumbprint |  | ||||||
| func ThumbprintPublicKey(pub PublicKey) string { |  | ||||||
| 	return ThumbprintUntypedPublicKey(pub.Key()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ThumbprintUntypedPublicKey is a non-typesafe version of ThumbprintPublicKey |  | ||||||
| // (but will still panic, to help you discover bugs in development rather than production). |  | ||||||
| func ThumbprintUntypedPublicKey(pub crypto.PublicKey) string { |  | ||||||
| 	switch p := pub.(type) { |  | ||||||
| 	case PublicKey: |  | ||||||
| 		return ThumbprintUntypedPublicKey(p.Key()) |  | ||||||
| 	case *ecdsa.PublicKey: |  | ||||||
| 		return ThumbprintECPublicKey(p) |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		return ThumbprintRSAPublicKey(p) |  | ||||||
| 	default: |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalECPublicKey will take an EC key and output a JWK, with optional expiration date |  | ||||||
| func MarshalECPublicKey(k *ecdsa.PublicKey, exp ...time.Time) []byte { |  | ||||||
| 	thumb := ThumbprintECPublicKey(k) |  | ||||||
| 	crv := k.Curve.Params().Name |  | ||||||
| 	x := base64.RawURLEncoding.EncodeToString(k.X.Bytes()) |  | ||||||
| 	y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes()) |  | ||||||
| 	expstr := "" |  | ||||||
| 	if 0 != len(exp) { |  | ||||||
| 		expstr = fmt.Sprintf(`"exp":%d,`, exp[0].Unix()) |  | ||||||
| 	} |  | ||||||
| 	return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"crv":%q,"kty":"EC","x":%q,"y":%q}`, thumb, expstr, crv, x, y)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalECPublicKeyWithoutKeyID will output the most minimal version of an EC JWK (no key id, no "use" flag, nada) |  | ||||||
| func MarshalECPublicKeyWithoutKeyID(k *ecdsa.PublicKey) []byte { |  | ||||||
| 	crv := k.Curve.Params().Name |  | ||||||
| 	x := base64.RawURLEncoding.EncodeToString(k.X.Bytes()) |  | ||||||
| 	y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes()) |  | ||||||
| 	return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, crv, x, y)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ThumbprintECPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key |  | ||||||
| func ThumbprintECPublicKey(k *ecdsa.PublicKey) string { |  | ||||||
| 	thumbprintable := MarshalECPublicKeyWithoutKeyID(k) |  | ||||||
| 	sha := sha256.Sum256(thumbprintable) |  | ||||||
| 	return base64.RawURLEncoding.EncodeToString(sha[:]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalRSAPublicKey will take an RSA key and output a JWK, with optional expiration date |  | ||||||
| func MarshalRSAPublicKey(p *rsa.PublicKey, exp ...time.Time) []byte { |  | ||||||
| 	thumb := ThumbprintRSAPublicKey(p) |  | ||||||
| 	e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()) |  | ||||||
| 	n := base64.RawURLEncoding.EncodeToString(p.N.Bytes()) |  | ||||||
| 	expstr := "" |  | ||||||
| 	if 0 != len(exp) { |  | ||||||
| 		expstr = fmt.Sprintf(`"exp":%d,`, exp[0].Unix()) |  | ||||||
| 	} |  | ||||||
| 	return []byte(fmt.Sprintf(`{"kid":%q,"use":"sig",%s"e":%q,"kty":"RSA","n":%q}`, thumb, expstr, e, n)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalRSAPublicKeyWithoutKeyID will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada) |  | ||||||
| func MarshalRSAPublicKeyWithoutKeyID(p *rsa.PublicKey) []byte { |  | ||||||
| 	e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()) |  | ||||||
| 	n := base64.RawURLEncoding.EncodeToString(p.N.Bytes()) |  | ||||||
| 	return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, e, n)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ThumbprintRSAPublicKey will output a RFC-spec SHA256 JWK thumbprint of an EC public key |  | ||||||
| func ThumbprintRSAPublicKey(p *rsa.PublicKey) string { |  | ||||||
| 	thumbprintable := MarshalRSAPublicKeyWithoutKeyID(p) |  | ||||||
| 	sha := sha256.Sum256([]byte(thumbprintable)) |  | ||||||
| 	return base64.RawURLEncoding.EncodeToString(sha[:]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParsePrivateKey will try to parse the bytes you give it |  | ||||||
| // in any of the supported formats: PEM, DER, PKCS8, PKCS1, SEC1, and JWK |  | ||||||
| func ParsePrivateKey(block []byte) (PrivateKey, error) { |  | ||||||
| 	blocks, err := getPEMBytes(block) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, ErrParsePrivateKey |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Parse PEM blocks (openssl generates junk metadata blocks for ECs) |  | ||||||
| 	// or the original DER, or the JWK |  | ||||||
| 	for i := range blocks { |  | ||||||
| 		block = blocks[i] |  | ||||||
| 		if key, err := parsePrivateKey(block); nil == err { |  | ||||||
| 			return key, nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i := range blocks { |  | ||||||
| 		block = blocks[i] |  | ||||||
| 		if _, err := parsePublicKey(block); nil == err { |  | ||||||
| 			return nil, ErrUnexpectedPublicKey |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// If we didn't parse a key arleady, we failed |  | ||||||
| 	return nil, ErrParsePrivateKey |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParsePrivateKeyString calls ParsePrivateKey([]byte(key)) for all you lazy folk. |  | ||||||
| func ParsePrivateKeyString(block string) (PrivateKey, error) { |  | ||||||
| 	return ParsePrivateKey([]byte(block)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parsePrivateKey(der []byte) (PrivateKey, error) { |  | ||||||
| 	var key PrivateKey |  | ||||||
| 
 |  | ||||||
| 	//fmt.Println("1. ParsePKCS8PrivateKey") |  | ||||||
| 	xkey, err := x509.ParsePKCS8PrivateKey(der) |  | ||||||
| 	if nil == err { |  | ||||||
| 		switch k := xkey.(type) { |  | ||||||
| 		case *rsa.PrivateKey: |  | ||||||
| 			key = k |  | ||||||
| 		case *ecdsa.PrivateKey: |  | ||||||
| 			key = k |  | ||||||
| 		default: |  | ||||||
| 			err = errors.New("Only RSA and ECDSA (EC) Private Keys are supported") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if nil != err { |  | ||||||
| 		//fmt.Println("2. ParseECPrivateKey") |  | ||||||
| 		key, err = x509.ParseECPrivateKey(der) |  | ||||||
| 		if nil != err { |  | ||||||
| 			//fmt.Println("3. ParsePKCS1PrivateKey") |  | ||||||
| 			key, err = x509.ParsePKCS1PrivateKey(der) |  | ||||||
| 			if nil != err { |  | ||||||
| 				//fmt.Println("4. ParseJWKPrivateKey") |  | ||||||
| 				key, err = ParseJWKPrivateKey(der) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// But did you know? |  | ||||||
| 	// You must return nil explicitly for interfaces |  | ||||||
| 	// https://golang.org/doc/faq#nil_error |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return key, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func getPEMBytes(block []byte) ([][]byte, error) { |  | ||||||
| 	var pemblock *pem.Block |  | ||||||
| 	var blocks = make([][]byte, 0, 1) |  | ||||||
| 
 |  | ||||||
| 	// Parse the PEM, if it's a pem |  | ||||||
| 	for { |  | ||||||
| 		pemblock, block = pem.Decode(block) |  | ||||||
| 		if nil != pemblock { |  | ||||||
| 			// got one block, there may be more |  | ||||||
| 			blocks = append(blocks, pemblock.Bytes) |  | ||||||
| 		} else { |  | ||||||
| 			// the last block was not a PEM block |  | ||||||
| 			// therefore the next isn't either |  | ||||||
| 			if 0 != len(block) { |  | ||||||
| 				blocks = append(blocks, block) |  | ||||||
| 			} |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(blocks) > 0 { |  | ||||||
| 		return blocks, nil |  | ||||||
| 	} |  | ||||||
| 	return nil, errors.New("no PEM blocks found") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParsePublicKey will try to parse the bytes you give it |  | ||||||
| // in any of the supported formats: PEM, DER, PKIX/SPKI, PKCS1, x509 Certificate, and JWK |  | ||||||
| func ParsePublicKey(block []byte) (PublicKey, error) { |  | ||||||
| 	blocks, err := getPEMBytes(block) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, ErrParsePublicKey |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Parse PEM blocks (openssl generates junk metadata blocks for ECs) |  | ||||||
| 	// or the original DER, or the JWK |  | ||||||
| 	for i := range blocks { |  | ||||||
| 		block = blocks[i] |  | ||||||
| 		if key, err := parsePublicKey(block); nil == err { |  | ||||||
| 			return key, nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i := range blocks { |  | ||||||
| 		block = blocks[i] |  | ||||||
| 		if _, err := parsePrivateKey(block); nil == err { |  | ||||||
| 			return nil, ErrUnexpectedPrivateKey |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// If we didn't parse a key arleady, we failed |  | ||||||
| 	return nil, ErrParsePublicKey |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParsePublicKeyString calls ParsePublicKey([]byte(key)) for all you lazy folk. |  | ||||||
| func ParsePublicKeyString(block string) (PublicKey, error) { |  | ||||||
| 	return ParsePublicKey([]byte(block)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parsePublicKey(der []byte) (PublicKey, error) { |  | ||||||
| 	cert, err := x509.ParseCertificate(der) |  | ||||||
| 	if nil == err { |  | ||||||
| 		switch k := cert.PublicKey.(type) { |  | ||||||
| 		case *rsa.PublicKey: |  | ||||||
| 			return NewPublicKey(k), nil |  | ||||||
| 		case *ecdsa.PublicKey: |  | ||||||
| 			return NewPublicKey(k), nil |  | ||||||
| 		default: |  | ||||||
| 			return nil, errors.New("Only RSA and ECDSA (EC) Public Keys are supported") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	//fmt.Println("1. ParsePKIXPublicKey") |  | ||||||
| 	xkey, err := x509.ParsePKIXPublicKey(der) |  | ||||||
| 	if nil == err { |  | ||||||
| 		switch k := xkey.(type) { |  | ||||||
| 		case *rsa.PublicKey: |  | ||||||
| 			return NewPublicKey(k), nil |  | ||||||
| 		case *ecdsa.PublicKey: |  | ||||||
| 			return NewPublicKey(k), nil |  | ||||||
| 		default: |  | ||||||
| 			return nil, errors.New("Only RSA and ECDSA (EC) Public Keys are supported") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	//fmt.Println("3. ParsePKCS1PrublicKey") |  | ||||||
| 	rkey, err := x509.ParsePKCS1PublicKey(der) |  | ||||||
| 	if nil == err { |  | ||||||
| 		//fmt.Println("4. ParseJWKPublicKey") |  | ||||||
| 		return NewPublicKey(rkey), nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return ParseJWKPublicKey(der) |  | ||||||
| 
 |  | ||||||
| 	/* |  | ||||||
| 		// But did you know? |  | ||||||
| 		// You must return nil explicitly for interfaces |  | ||||||
| 		// https://golang.org/doc/faq#nil_error |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	*/ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewJWKPublicKey contstructs a PublicKey from the relevant pieces a map[string]string (generic JSON) |  | ||||||
| func NewJWKPublicKey(m map[string]string) (PublicKey, error) { |  | ||||||
| 	switch m["kty"] { |  | ||||||
| 	case "RSA": |  | ||||||
| 		return parseRSAPublicKey(m) |  | ||||||
| 	case "EC": |  | ||||||
| 		return parseECPublicKey(m) |  | ||||||
| 	default: |  | ||||||
| 		return nil, ErrInvalidKeyType |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message |  | ||||||
| func ParseJWKPublicKey(b []byte) (PublicKey, error) { |  | ||||||
| 	// RSA and EC have "d" as a private part |  | ||||||
| 	if bytes.Contains(b, []byte(`"d"`)) { |  | ||||||
| 		return nil, ErrUnexpectedPrivateKey |  | ||||||
| 	} |  | ||||||
| 	return newJWKPublicKey(b) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk. |  | ||||||
| func ParseJWKPublicKeyString(s string) (PublicKey, error) { |  | ||||||
| 	if strings.Contains(s, `"d"`) { |  | ||||||
| 		return nil, ErrUnexpectedPrivateKey |  | ||||||
| 	} |  | ||||||
| 	return newJWKPublicKey(s) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message |  | ||||||
| func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) { |  | ||||||
| 	m := make(map[string]string) |  | ||||||
| 	if err := json.NewDecoder(r).Decode(&m); nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	if d := m["d"]; "" != d { |  | ||||||
| 		return nil, ErrUnexpectedPrivateKey |  | ||||||
| 	} |  | ||||||
| 	return newJWKPublicKey(m) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // the underpinnings of the parser as used by the typesafe wrappers |  | ||||||
| func newJWKPublicKey(data interface{}) (PublicKey, error) { |  | ||||||
| 	var m map[string]string |  | ||||||
| 
 |  | ||||||
| 	switch d := data.(type) { |  | ||||||
| 	case map[string]string: |  | ||||||
| 		m = d |  | ||||||
| 	case string: |  | ||||||
| 		if err := json.Unmarshal([]byte(d), &m); nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	case []byte: |  | ||||||
| 		if err := json.Unmarshal(d, &m); nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		panic("Developer Error: unsupported interface type") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return NewJWKPublicKey(m) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ParseJWKPrivateKey parses a JSON-encoded JWK and returns a PrivateKey, or a (hopefully) helpful error message |  | ||||||
| func ParseJWKPrivateKey(b []byte) (PrivateKey, error) { |  | ||||||
| 	var m map[string]string |  | ||||||
| 	if err := json.Unmarshal(b, &m); nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch m["kty"] { |  | ||||||
| 	case "RSA": |  | ||||||
| 		return parseRSAPrivateKey(m) |  | ||||||
| 	case "EC": |  | ||||||
| 		return parseECPrivateKey(m) |  | ||||||
| 	default: |  | ||||||
| 		return nil, ErrInvalidKeyType |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseRSAPublicKey(m map[string]string) (*RSAPublicKey, error) { |  | ||||||
| 	// TODO grab expiry? |  | ||||||
| 	kid, _ := m["kid"] |  | ||||||
| 	n, _ := base64.RawURLEncoding.DecodeString(m["n"]) |  | ||||||
| 	e, _ := base64.RawURLEncoding.DecodeString(m["e"]) |  | ||||||
| 	if 0 == len(n) || 0 == len(e) { |  | ||||||
| 		return nil, ErrParseJWK |  | ||||||
| 	} |  | ||||||
| 	ni := &big.Int{} |  | ||||||
| 	ni.SetBytes(n) |  | ||||||
| 	ei := &big.Int{} |  | ||||||
| 	ei.SetBytes(e) |  | ||||||
| 
 |  | ||||||
| 	pub := &rsa.PublicKey{ |  | ||||||
| 		N: ni, |  | ||||||
| 		E: int(ei.Int64()), |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return &RSAPublicKey{ |  | ||||||
| 		PublicKey: pub, |  | ||||||
| 		KID:       kid, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseRSAPrivateKey(m map[string]string) (key *rsa.PrivateKey, err error) { |  | ||||||
| 	pub, err := parseRSAPublicKey(m) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	d, _ := base64.RawURLEncoding.DecodeString(m["d"]) |  | ||||||
| 	p, _ := base64.RawURLEncoding.DecodeString(m["p"]) |  | ||||||
| 	q, _ := base64.RawURLEncoding.DecodeString(m["q"]) |  | ||||||
| 	dp, _ := base64.RawURLEncoding.DecodeString(m["dp"]) |  | ||||||
| 	dq, _ := base64.RawURLEncoding.DecodeString(m["dq"]) |  | ||||||
| 	qinv, _ := base64.RawURLEncoding.DecodeString(m["qi"]) |  | ||||||
| 	if 0 == len(d) || 0 == len(p) || 0 == len(dp) || 0 == len(dq) || 0 == len(qinv) { |  | ||||||
| 		return nil, ErrParseJWK |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	di := &big.Int{} |  | ||||||
| 	di.SetBytes(d) |  | ||||||
| 	pi := &big.Int{} |  | ||||||
| 	pi.SetBytes(p) |  | ||||||
| 	qi := &big.Int{} |  | ||||||
| 	qi.SetBytes(q) |  | ||||||
| 	dpi := &big.Int{} |  | ||||||
| 	dpi.SetBytes(dp) |  | ||||||
| 	dqi := &big.Int{} |  | ||||||
| 	dqi.SetBytes(dq) |  | ||||||
| 	qinvi := &big.Int{} |  | ||||||
| 	qinvi.SetBytes(qinv) |  | ||||||
| 
 |  | ||||||
| 	key = &rsa.PrivateKey{ |  | ||||||
| 		PublicKey: *pub.PublicKey, |  | ||||||
| 		D:         di, |  | ||||||
| 		Primes:    []*big.Int{pi, qi}, |  | ||||||
| 		Precomputed: rsa.PrecomputedValues{ |  | ||||||
| 			Dp:   dpi, |  | ||||||
| 			Dq:   dqi, |  | ||||||
| 			Qinv: qinvi, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseECPublicKey(m map[string]string) (*ECPublicKey, error) { |  | ||||||
| 	// TODO grab expiry? |  | ||||||
| 	kid, _ := m["kid"] |  | ||||||
| 	x, _ := base64.RawURLEncoding.DecodeString(m["x"]) |  | ||||||
| 	y, _ := base64.RawURLEncoding.DecodeString(m["y"]) |  | ||||||
| 	if 0 == len(x) || 0 == len(y) || 0 == len(m["crv"]) { |  | ||||||
| 		return nil, ErrParseJWK |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	xi := &big.Int{} |  | ||||||
| 	xi.SetBytes(x) |  | ||||||
| 
 |  | ||||||
| 	yi := &big.Int{} |  | ||||||
| 	yi.SetBytes(y) |  | ||||||
| 
 |  | ||||||
| 	var crv elliptic.Curve |  | ||||||
| 	switch m["crv"] { |  | ||||||
| 	case "P-256": |  | ||||||
| 		crv = elliptic.P256() |  | ||||||
| 	case "P-384": |  | ||||||
| 		crv = elliptic.P384() |  | ||||||
| 	case "P-521": |  | ||||||
| 		crv = elliptic.P521() |  | ||||||
| 	default: |  | ||||||
| 		return nil, ErrInvalidCurve |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	pub := &ecdsa.PublicKey{ |  | ||||||
| 		Curve: crv, |  | ||||||
| 		X:     xi, |  | ||||||
| 		Y:     yi, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return &ECPublicKey{ |  | ||||||
| 		PublicKey: pub, |  | ||||||
| 		KID:       kid, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseECPrivateKey(m map[string]string) (*ecdsa.PrivateKey, error) { |  | ||||||
| 	pub, err := parseECPublicKey(m) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	d, _ := base64.RawURLEncoding.DecodeString(m["d"]) |  | ||||||
| 	if 0 == len(d) { |  | ||||||
| 		return nil, ErrParseJWK |  | ||||||
| 	} |  | ||||||
| 	di := &big.Int{} |  | ||||||
| 	di.SetBytes(d) |  | ||||||
| 
 |  | ||||||
| 	return &ecdsa.PrivateKey{ |  | ||||||
| 		PublicKey: *pub.PublicKey, |  | ||||||
| 		D:         di, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
							
								
								
									
										171
									
								
								vendor/git.rootprojects.org/root/keypairs/marshal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										171
									
								
								vendor/git.rootprojects.org/root/keypairs/marshal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,171 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"crypto" |  | ||||||
| 	"crypto/ecdsa" |  | ||||||
| 	"crypto/rsa" |  | ||||||
| 	"crypto/x509" |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"encoding/pem" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| 	"math/big" |  | ||||||
| 	mathrand "math/rand" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // MarshalPEMPublicKey outputs the given public key as JWK |  | ||||||
| func MarshalPEMPublicKey(pubkey crypto.PublicKey) ([]byte, error) { |  | ||||||
| 	block, err := marshalDERPublicKey(pubkey) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return pem.EncodeToMemory(block), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalDERPublicKey outputs the given public key as JWK |  | ||||||
| func MarshalDERPublicKey(pubkey crypto.PublicKey) ([]byte, error) { |  | ||||||
| 	block, err := marshalDERPublicKey(pubkey) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return block.Bytes, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // marshalDERPublicKey outputs the given public key as JWK |  | ||||||
| func marshalDERPublicKey(pubkey crypto.PublicKey) (*pem.Block, error) { |  | ||||||
| 
 |  | ||||||
| 	var der []byte |  | ||||||
| 	var typ string |  | ||||||
| 	var err error |  | ||||||
| 	switch k := pubkey.(type) { |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		der = x509.MarshalPKCS1PublicKey(k) |  | ||||||
| 		typ = "RSA PUBLIC KEY" |  | ||||||
| 	case *ecdsa.PublicKey: |  | ||||||
| 		typ = "PUBLIC KEY" |  | ||||||
| 		der, err = x509.MarshalPKIXPublicKey(k) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		panic("Developer Error: impossible key type") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return &pem.Block{ |  | ||||||
| 		Bytes: der, |  | ||||||
| 		Type:  typ, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalJWKPrivateKey outputs the given private key as JWK |  | ||||||
| func MarshalJWKPrivateKey(privkey PrivateKey) []byte { |  | ||||||
| 	// thumbprint keys are alphabetically sorted and only include the necessary public parts |  | ||||||
| 	switch k := privkey.(type) { |  | ||||||
| 	case *rsa.PrivateKey: |  | ||||||
| 		return MarshalRSAPrivateKey(k) |  | ||||||
| 	case *ecdsa.PrivateKey: |  | ||||||
| 		return MarshalECPrivateKey(k) |  | ||||||
| 	default: |  | ||||||
| 		// this is unreachable because we know the types that we pass in |  | ||||||
| 		log.Printf("keytype: %t, %+v\n", privkey, privkey) |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 		//return nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalDERPrivateKey outputs the given private key as ASN.1 DER |  | ||||||
| func MarshalDERPrivateKey(privkey PrivateKey) ([]byte, error) { |  | ||||||
| 	// thumbprint keys are alphabetically sorted and only include the necessary public parts |  | ||||||
| 	switch k := privkey.(type) { |  | ||||||
| 	case *rsa.PrivateKey: |  | ||||||
| 		return x509.MarshalPKCS1PrivateKey(k), nil |  | ||||||
| 	case *ecdsa.PrivateKey: |  | ||||||
| 		return x509.MarshalECPrivateKey(k) |  | ||||||
| 	default: |  | ||||||
| 		// this is unreachable because we know the types that we pass in |  | ||||||
| 		log.Printf("keytype: %t, %+v\n", privkey, privkey) |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 		//return nil, nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func marshalDERPrivateKey(privkey PrivateKey) (*pem.Block, error) { |  | ||||||
| 	var typ string |  | ||||||
| 	var bytes []byte |  | ||||||
| 	var err error |  | ||||||
| 
 |  | ||||||
| 	switch k := privkey.(type) { |  | ||||||
| 	case *rsa.PrivateKey: |  | ||||||
| 		if 0 == mathrand.Intn(2) { |  | ||||||
| 			typ = "PRIVATE KEY" |  | ||||||
| 			bytes, err = x509.MarshalPKCS8PrivateKey(k) |  | ||||||
| 			if nil != err { |  | ||||||
| 				return nil, err |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			typ = "RSA PRIVATE KEY" |  | ||||||
| 			bytes = x509.MarshalPKCS1PrivateKey(k) |  | ||||||
| 		} |  | ||||||
| 		return &pem.Block{ |  | ||||||
| 			Type:  typ, |  | ||||||
| 			Bytes: bytes, |  | ||||||
| 		}, nil |  | ||||||
| 	case *ecdsa.PrivateKey: |  | ||||||
| 		if 0 == mathrand.Intn(2) { |  | ||||||
| 			typ = "PRIVATE KEY" |  | ||||||
| 			bytes, err = x509.MarshalPKCS8PrivateKey(k) |  | ||||||
| 		} else { |  | ||||||
| 			typ = "EC PRIVATE KEY" |  | ||||||
| 			bytes, err = x509.MarshalECPrivateKey(k) |  | ||||||
| 		} |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		return &pem.Block{ |  | ||||||
| 			Type:  typ, |  | ||||||
| 			Bytes: bytes, |  | ||||||
| 		}, nil |  | ||||||
| 	default: |  | ||||||
| 		// this is unreachable because we know the types that we pass in |  | ||||||
| 		log.Printf("keytype: %t, %+v\n", privkey, privkey) |  | ||||||
| 		panic(ErrInvalidPublicKey) |  | ||||||
| 		//return nil, nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalPEMPrivateKey outputs the given private key as ASN.1 PEM |  | ||||||
| func MarshalPEMPrivateKey(privkey PrivateKey) ([]byte, error) { |  | ||||||
| 	block, err := marshalDERPrivateKey(privkey) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return pem.EncodeToMemory(block), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalECPrivateKey will output the given private key as JWK |  | ||||||
| func MarshalECPrivateKey(k *ecdsa.PrivateKey) []byte { |  | ||||||
| 	crv := k.Curve.Params().Name |  | ||||||
| 	d := base64.RawURLEncoding.EncodeToString(k.D.Bytes()) |  | ||||||
| 	x := base64.RawURLEncoding.EncodeToString(k.X.Bytes()) |  | ||||||
| 	y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes()) |  | ||||||
| 	return []byte(fmt.Sprintf( |  | ||||||
| 		`{"crv":%q,"d":%q,"kty":"EC","x":%q,"y":%q}`, |  | ||||||
| 		crv, d, x, y, |  | ||||||
| 	)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MarshalRSAPrivateKey will output the given private key as JWK |  | ||||||
| func MarshalRSAPrivateKey(pk *rsa.PrivateKey) []byte { |  | ||||||
| 	e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pk.E)).Bytes()) |  | ||||||
| 	n := base64.RawURLEncoding.EncodeToString(pk.N.Bytes()) |  | ||||||
| 	d := base64.RawURLEncoding.EncodeToString(pk.D.Bytes()) |  | ||||||
| 	p := base64.RawURLEncoding.EncodeToString(pk.Primes[0].Bytes()) |  | ||||||
| 	q := base64.RawURLEncoding.EncodeToString(pk.Primes[1].Bytes()) |  | ||||||
| 	dp := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Dp.Bytes()) |  | ||||||
| 	dq := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Dq.Bytes()) |  | ||||||
| 	qi := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Qinv.Bytes()) |  | ||||||
| 	return []byte(fmt.Sprintf( |  | ||||||
| 		`{"d":%q,"dp":%q,"dq":%q,"e":%q,"kty":"RSA","n":%q,"p":%q,"q":%q,"qi":%q}`, |  | ||||||
| 		d, dp, dq, e, n, p, q, qi, |  | ||||||
| 	)) |  | ||||||
| } |  | ||||||
							
								
								
									
										46
									
								
								vendor/git.rootprojects.org/root/keypairs/mock.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/git.rootprojects.org/root/keypairs/mock.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,46 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"crypto/rsa" |  | ||||||
| 	"io" |  | ||||||
| 	"log" |  | ||||||
| 	mathrand "math/rand" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // this shananigans is only for testing and debug API stuff |  | ||||||
| func (o *keyOptions) maybeMockReader() io.Reader { |  | ||||||
| 	if !allowMocking { |  | ||||||
| 		panic("mock method called when mocking is not allowed") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if 0 == o.mockSeed { |  | ||||||
| 		return randReader |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	log.Println("WARNING: MOCK: using insecure reader") |  | ||||||
| 	return mathrand.New(mathrand.NewSource(o.mockSeed)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const maxRetry = 16 |  | ||||||
| 
 |  | ||||||
| func maybeDerandomizeMockKey(privkey PrivateKey, keylen int, opts *keyOptions) PrivateKey { |  | ||||||
| 	if 0 != opts.mockSeed { |  | ||||||
| 		for i := 0; i < maxRetry; i++ { |  | ||||||
| 			otherkey, _ := rsa.GenerateKey(opts.nextReader(), keylen) |  | ||||||
| 			otherCmp := otherkey.D.Cmp(privkey.(*rsa.PrivateKey).D) |  | ||||||
| 			if 0 != otherCmp { |  | ||||||
| 				// There are two possible keys, choose the lesser D value |  | ||||||
| 				// See https://github.com/square/go-jose/issues/189 |  | ||||||
| 				if otherCmp < 0 { |  | ||||||
| 					privkey = otherkey |  | ||||||
| 				} |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			if maxRetry == i-1 { |  | ||||||
| 				log.Printf("error: coinflip landed on heads %d times", maxRetry) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return privkey |  | ||||||
| } |  | ||||||
							
								
								
									
										165
									
								
								vendor/git.rootprojects.org/root/keypairs/sign.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										165
									
								
								vendor/git.rootprojects.org/root/keypairs/sign.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,165 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"crypto" |  | ||||||
| 	"crypto/ecdsa" |  | ||||||
| 	"crypto/rsa" |  | ||||||
| 	"crypto/sha256" |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	mathrand "math/rand" // to be used for good, not evil |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Object is a type alias representing generic JSON data |  | ||||||
| type Object = map[string]interface{} |  | ||||||
| 
 |  | ||||||
| // SignClaims adds `typ`, `kid` (or `jwk`), and `alg` in the header and expects claims for `jti`, `exp`, `iss`, and `iat` |  | ||||||
| func SignClaims(privkey PrivateKey, header Object, claims Object) (*JWS, error) { |  | ||||||
| 	var randsrc io.Reader = randReader |  | ||||||
| 	seed, _ := header["_seed"].(int64) |  | ||||||
| 	if 0 != seed { |  | ||||||
| 		randsrc = mathrand.New(mathrand.NewSource(seed)) |  | ||||||
| 		//delete(header, "_seed") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	protected, header, err := headerToProtected(NewPublicKey(privkey.Public()), header) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	protected64 := base64.RawURLEncoding.EncodeToString(protected) |  | ||||||
| 
 |  | ||||||
| 	payload, err := claimsToPayload(claims) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	payload64 := base64.RawURLEncoding.EncodeToString(payload) |  | ||||||
| 
 |  | ||||||
| 	signable := fmt.Sprintf(`%s.%s`, protected64, payload64) |  | ||||||
| 	hash := sha256.Sum256([]byte(signable)) |  | ||||||
| 
 |  | ||||||
| 	sig := Sign(privkey, hash[:], randsrc) |  | ||||||
| 	sig64 := base64.RawURLEncoding.EncodeToString(sig) |  | ||||||
| 	//log.Printf("\n(Sign)\nSignable: %s", signable) |  | ||||||
| 	//log.Printf("Hash: %s", hash) |  | ||||||
| 	//log.Printf("Sig: %s", sig64) |  | ||||||
| 
 |  | ||||||
| 	return &JWS{ |  | ||||||
| 		Header:    header, |  | ||||||
| 		Claims:    claims, |  | ||||||
| 		Protected: protected64, |  | ||||||
| 		Payload:   payload64, |  | ||||||
| 		Signature: sig64, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func headerToProtected(pub PublicKey, header Object) ([]byte, Object, error) { |  | ||||||
| 	if nil == header { |  | ||||||
| 		header = Object{} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Only supporting 2048-bit and P256 keys right now |  | ||||||
| 	// because that's all that's practical and well-supported. |  | ||||||
| 	// No security theatre here. |  | ||||||
| 	alg := "ES256" |  | ||||||
| 	switch pub.Key().(type) { |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		alg = "RS256" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if selfSign, _ := header["_jwk"].(bool); selfSign { |  | ||||||
| 		delete(header, "_jwk") |  | ||||||
| 		any := Object{} |  | ||||||
| 		_ = json.Unmarshal(MarshalJWKPublicKey(pub), &any) |  | ||||||
| 		header["jwk"] = any |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// TODO what are the acceptable values? JWT. JWS? others? |  | ||||||
| 	header["typ"] = "JWT" |  | ||||||
| 	if _, ok := header["jwk"]; !ok { |  | ||||||
| 		thumbprint := ThumbprintPublicKey(pub) |  | ||||||
| 		kid, _ := header["kid"].(string) |  | ||||||
| 		if "" != kid && thumbprint != kid { |  | ||||||
| 			return nil, nil, errors.New("'kid' should be the key's thumbprint") |  | ||||||
| 		} |  | ||||||
| 		header["kid"] = thumbprint |  | ||||||
| 	} |  | ||||||
| 	header["alg"] = alg |  | ||||||
| 
 |  | ||||||
| 	protected, err := json.Marshal(header) |  | ||||||
| 	if nil != err { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 	return protected, header, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func claimsToPayload(claims Object) ([]byte, error) { |  | ||||||
| 	if nil == claims { |  | ||||||
| 		claims = Object{} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var dur time.Duration |  | ||||||
| 	jti, _ := claims["jti"].(string) |  | ||||||
| 	insecure, _ := claims["insecure"].(bool) |  | ||||||
| 
 |  | ||||||
| 	switch exp := claims["exp"].(type) { |  | ||||||
| 	case time.Duration: |  | ||||||
| 		// TODO: MUST this go first? |  | ||||||
| 		// int64(time.Duration) vs time.Duration(int64) |  | ||||||
| 		dur = exp |  | ||||||
| 	case string: |  | ||||||
| 		var err error |  | ||||||
| 		dur, err = time.ParseDuration(exp) |  | ||||||
| 		// TODO s, err := time.ParseDuration(dur) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	case int: |  | ||||||
| 		dur = time.Second * time.Duration(exp) |  | ||||||
| 	case int64: |  | ||||||
| 		dur = time.Second * time.Duration(exp) |  | ||||||
| 	case float64: |  | ||||||
| 		dur = time.Second * time.Duration(exp) |  | ||||||
| 	default: |  | ||||||
| 		dur = 0 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if "" == jti && 0 == dur && !insecure { |  | ||||||
| 		return nil, errors.New("token must have jti or exp as to be expirable / cancellable") |  | ||||||
| 	} |  | ||||||
| 	claims["exp"] = time.Now().Add(dur).Unix() |  | ||||||
| 
 |  | ||||||
| 	return json.Marshal(claims) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Sign signs both RSA and ECDSA. Use `nil` or `crypto/rand.Reader` except for debugging. |  | ||||||
| func Sign(privkey PrivateKey, hash []byte, rand io.Reader) []byte { |  | ||||||
| 	if nil == rand { |  | ||||||
| 		rand = randReader |  | ||||||
| 	} |  | ||||||
| 	var sig []byte |  | ||||||
| 
 |  | ||||||
| 	if len(hash) != 32 { |  | ||||||
| 		panic("only 256-bit hashes for 2048-bit and 256-bit keys are supported") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch k := privkey.(type) { |  | ||||||
| 	case *rsa.PrivateKey: |  | ||||||
| 		sig, _ = rsa.SignPKCS1v15(rand, k, crypto.SHA256, hash) |  | ||||||
| 	case *ecdsa.PrivateKey: |  | ||||||
| 		r, s, _ := ecdsa.Sign(rand, k, hash[:]) |  | ||||||
| 		rb := r.Bytes() |  | ||||||
| 		for len(rb) < 32 { |  | ||||||
| 			rb = append([]byte{0}, rb...) |  | ||||||
| 		} |  | ||||||
| 		sb := s.Bytes() |  | ||||||
| 		for len(rb) < 32 { |  | ||||||
| 			sb = append([]byte{0}, sb...) |  | ||||||
| 		} |  | ||||||
| 		sig = append(rb, sb...) |  | ||||||
| 	} |  | ||||||
| 	return sig |  | ||||||
| } |  | ||||||
							
								
								
									
										174
									
								
								vendor/git.rootprojects.org/root/keypairs/verify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										174
									
								
								vendor/git.rootprojects.org/root/keypairs/verify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,174 +0,0 @@ | |||||||
| package keypairs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"crypto" |  | ||||||
| 	"crypto/ecdsa" |  | ||||||
| 	"crypto/rsa" |  | ||||||
| 	"crypto/sha256" |  | ||||||
| 	"crypto/subtle" |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| 	"math/big" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // VerifyClaims will check the signature of a parsed JWT |  | ||||||
| func VerifyClaims(pubkey PublicKey, jws *JWS) (errs []error) { |  | ||||||
| 	kid, _ := jws.Header["kid"].(string) |  | ||||||
| 	jwkmap, hasJWK := jws.Header["jwk"].(Object) |  | ||||||
| 	//var jwk JWK = nil |  | ||||||
| 
 |  | ||||||
| 	seed, _ := jws.Header["_seed"].(int64) |  | ||||||
| 	seedf64, _ := jws.Header["_seed"].(float64) |  | ||||||
| 	kty, _ := jws.Header["_kty"].(string) |  | ||||||
| 	if 0 == seed { |  | ||||||
| 		seed = int64(seedf64) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var pub PublicKey = nil |  | ||||||
| 	if hasJWK { |  | ||||||
| 		pub, errs = selfsignCheck(jwkmap, errs) |  | ||||||
| 	} else { |  | ||||||
| 		opts := &keyOptions{mockSeed: seed, KeyType: kty} |  | ||||||
| 		pub, errs = pubkeyCheck(pubkey, kid, opts, errs) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jti, _ := jws.Claims["jti"].(string) |  | ||||||
| 	expf64, _ := jws.Claims["exp"].(float64) |  | ||||||
| 	exp := int64(expf64) |  | ||||||
| 	if 0 == exp { |  | ||||||
| 		if "" == jti { |  | ||||||
| 			err := errors.New("one of 'jti' or 'exp' must exist for token expiry") |  | ||||||
| 			errs = append(errs, err) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		if time.Now().Unix() > exp { |  | ||||||
| 			err := fmt.Errorf("token expired at %d (%s)", exp, time.Unix(exp, 0)) |  | ||||||
| 			errs = append(errs, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	signable := fmt.Sprintf("%s.%s", jws.Protected, jws.Payload) |  | ||||||
| 	hash := sha256.Sum256([]byte(signable)) |  | ||||||
| 	sig, err := base64.RawURLEncoding.DecodeString(jws.Signature) |  | ||||||
| 	if nil != err { |  | ||||||
| 		err := fmt.Errorf("could not decode signature: %w", err) |  | ||||||
| 		errs = append(errs, err) |  | ||||||
| 		return errs |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	//log.Printf("\n(Verify)\nSignable: %s", signable) |  | ||||||
| 	//log.Printf("Hash: %s", hash) |  | ||||||
| 	//log.Printf("Sig: %s", jws.Signature) |  | ||||||
| 	if nil == pub { |  | ||||||
| 		err := fmt.Errorf("token signature could not be verified") |  | ||||||
| 		errs = append(errs, err) |  | ||||||
| 	} else if !Verify(pub, hash[:], sig) { |  | ||||||
| 		err := fmt.Errorf("token signature is not valid") |  | ||||||
| 		errs = append(errs, err) |  | ||||||
| 	} |  | ||||||
| 	return errs |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func selfsignCheck(jwkmap Object, errs []error) (PublicKey, []error) { |  | ||||||
| 	var pub PublicKey = nil |  | ||||||
| 	log.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk'") |  | ||||||
| 	log.Println("Security TODO: did not check jws.Claims[\"iss\"]") |  | ||||||
| 	kty := jwkmap["kty"] |  | ||||||
| 	var err error |  | ||||||
| 	if "RSA" == kty { |  | ||||||
| 		e, _ := jwkmap["e"].(string) |  | ||||||
| 		n, _ := jwkmap["n"].(string) |  | ||||||
| 		k, _ := (&RSAJWK{ |  | ||||||
| 			Exp: e, |  | ||||||
| 			N:   n, |  | ||||||
| 		}).marshalJWK() |  | ||||||
| 		pub, err = ParseJWKPublicKey(k) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, append(errs, err) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		crv, _ := jwkmap["crv"].(string) |  | ||||||
| 		x, _ := jwkmap["x"].(string) |  | ||||||
| 		y, _ := jwkmap["y"].(string) |  | ||||||
| 		k, _ := (&ECJWK{ |  | ||||||
| 			Curve: crv, |  | ||||||
| 			X:     x, |  | ||||||
| 			Y:     y, |  | ||||||
| 		}).marshalJWK() |  | ||||||
| 		pub, err = ParseJWKPublicKey(k) |  | ||||||
| 		if nil != err { |  | ||||||
| 			return nil, append(errs, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return pub, errs |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func pubkeyCheck(pubkey PublicKey, kid string, opts *keyOptions, errs []error) (PublicKey, []error) { |  | ||||||
| 	var pub PublicKey = nil |  | ||||||
| 
 |  | ||||||
| 	if "" == kid { |  | ||||||
| 		err := errors.New("token should have 'kid' or 'jwk' in header to identify the public key") |  | ||||||
| 		errs = append(errs, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if nil == pubkey { |  | ||||||
| 		if allowMocking { |  | ||||||
| 			if 0 == opts.mockSeed { |  | ||||||
| 				err := errors.New("the debug API requires '_seed' to accompany 'kid'") |  | ||||||
| 				errs = append(errs, err) |  | ||||||
| 			} |  | ||||||
| 			if "" == opts.KeyType { |  | ||||||
| 				err := errors.New("the debug API requires '_kty' to accompany '_seed'") |  | ||||||
| 				errs = append(errs, err) |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if 0 == opts.mockSeed || "" == opts.KeyType { |  | ||||||
| 				return nil, errs |  | ||||||
| 			} |  | ||||||
| 			privkey := newPrivateKey(opts) |  | ||||||
| 			pub = NewPublicKey(privkey.Public()) |  | ||||||
| 			return pub, errs |  | ||||||
| 		} |  | ||||||
| 		err := errors.New("no matching public key") |  | ||||||
| 		errs = append(errs, err) |  | ||||||
| 	} else { |  | ||||||
| 		pub = pubkey |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if nil != pub && "" != kid { |  | ||||||
| 		if 1 != subtle.ConstantTimeCompare([]byte(kid), []byte(pub.Thumbprint())) { |  | ||||||
| 			err := errors.New("'kid' does not match the public key thumbprint") |  | ||||||
| 			errs = append(errs, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return pub, errs |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Verify will check the signature of a hash |  | ||||||
| func Verify(pubkey PublicKey, hash []byte, sig []byte) bool { |  | ||||||
| 
 |  | ||||||
| 	switch pub := pubkey.Key().(type) { |  | ||||||
| 	case *rsa.PublicKey: |  | ||||||
| 		//log.Printf("RSA VERIFY") |  | ||||||
| 		// TODO Size(key) to detect key size ? |  | ||||||
| 		//alg := "SHA256" |  | ||||||
| 		// TODO: this hasn't been tested yet |  | ||||||
| 		if err := rsa.VerifyPKCS1v15(pub, crypto.SHA256, hash, sig); nil != err { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 		return true |  | ||||||
| 	case *ecdsa.PublicKey: |  | ||||||
| 		r := &big.Int{} |  | ||||||
| 		r.SetBytes(sig[0:32]) |  | ||||||
| 		s := &big.Int{} |  | ||||||
| 		s.SetBytes(sig[32:]) |  | ||||||
| 		return ecdsa.Verify(pub, hash, r, s) |  | ||||||
| 	default: |  | ||||||
| 		panic("impossible condition: non-rsa/non-ecdsa key") |  | ||||||
| 		//return false |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/mod/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/mod/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,27 +0,0 @@ | |||||||
| Copyright (c) 2009 The Go Authors. All rights reserved. |  | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/mod/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/mod/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,22 +0,0 @@ | |||||||
| Additional IP Rights Grant (Patents) |  | ||||||
| 
 |  | ||||||
| "This implementation" means the copyrightable works distributed by |  | ||||||
| Google as part of the Go project. |  | ||||||
| 
 |  | ||||||
| Google hereby grants to You a perpetual, worldwide, non-exclusive, |  | ||||||
| no-charge, royalty-free, irrevocable (except as stated in this section) |  | ||||||
| patent license to make, have made, use, offer to sell, sell, import, |  | ||||||
| transfer and otherwise run, modify and propagate the contents of this |  | ||||||
| implementation of Go, where such license applies only to those patent |  | ||||||
| claims, both currently owned or controlled by Google and acquired in |  | ||||||
| the future, licensable by Google that are necessarily infringed by this |  | ||||||
| implementation of Go.  This grant does not include claims that would be |  | ||||||
| infringed only as a consequence of further modification of this |  | ||||||
| implementation.  If you or your agent or exclusive licensee institute or |  | ||||||
| order or agree to the institution of patent litigation against any |  | ||||||
| entity (including a cross-claim or counterclaim in a lawsuit) alleging |  | ||||||
| that this implementation of Go or any code incorporated within this |  | ||||||
| implementation of Go constitutes direct or contributory patent |  | ||||||
| infringement, or inducement of patent infringement, then any patent |  | ||||||
| rights granted to you under this License for this implementation of Go |  | ||||||
| shall terminate as of the date such litigation is filed. |  | ||||||
							
								
								
									
										388
									
								
								vendor/golang.org/x/mod/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										388
									
								
								vendor/golang.org/x/mod/semver/semver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,388 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package semver implements comparison of semantic version strings. |  | ||||||
| // In this package, semantic version strings must begin with a leading "v", |  | ||||||
| // as in "v1.0.0". |  | ||||||
| // |  | ||||||
| // The general form of a semantic version string accepted by this package is |  | ||||||
| // |  | ||||||
| //	vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] |  | ||||||
| // |  | ||||||
| // where square brackets indicate optional parts of the syntax; |  | ||||||
| // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; |  | ||||||
| // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers |  | ||||||
| // using only alphanumeric characters and hyphens; and |  | ||||||
| // all-numeric PRERELEASE identifiers must not have leading zeros. |  | ||||||
| // |  | ||||||
| // This package follows Semantic Versioning 2.0.0 (see semver.org) |  | ||||||
| // with two exceptions. First, it requires the "v" prefix. Second, it recognizes |  | ||||||
| // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) |  | ||||||
| // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. |  | ||||||
| package semver |  | ||||||
| 
 |  | ||||||
| // parsed returns the parsed form of a semantic version string. |  | ||||||
| type parsed struct { |  | ||||||
| 	major      string |  | ||||||
| 	minor      string |  | ||||||
| 	patch      string |  | ||||||
| 	short      string |  | ||||||
| 	prerelease string |  | ||||||
| 	build      string |  | ||||||
| 	err        string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsValid reports whether v is a valid semantic version string. |  | ||||||
| func IsValid(v string) bool { |  | ||||||
| 	_, ok := parse(v) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Canonical returns the canonical formatting of the semantic version v. |  | ||||||
| // It fills in any missing .MINOR or .PATCH and discards build metadata. |  | ||||||
| // Two semantic versions compare equal only if their canonical formattings |  | ||||||
| // are identical strings. |  | ||||||
| // The canonical invalid semantic version is the empty string. |  | ||||||
| func Canonical(v string) string { |  | ||||||
| 	p, ok := parse(v) |  | ||||||
| 	if !ok { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	if p.build != "" { |  | ||||||
| 		return v[:len(v)-len(p.build)] |  | ||||||
| 	} |  | ||||||
| 	if p.short != "" { |  | ||||||
| 		return v + p.short |  | ||||||
| 	} |  | ||||||
| 	return v |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Major returns the major version prefix of the semantic version v. |  | ||||||
| // For example, Major("v2.1.0") == "v2". |  | ||||||
| // If v is an invalid semantic version string, Major returns the empty string. |  | ||||||
| func Major(v string) string { |  | ||||||
| 	pv, ok := parse(v) |  | ||||||
| 	if !ok { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return v[:1+len(pv.major)] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // MajorMinor returns the major.minor version prefix of the semantic version v. |  | ||||||
| // For example, MajorMinor("v2.1.0") == "v2.1". |  | ||||||
| // If v is an invalid semantic version string, MajorMinor returns the empty string. |  | ||||||
| func MajorMinor(v string) string { |  | ||||||
| 	pv, ok := parse(v) |  | ||||||
| 	if !ok { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	i := 1 + len(pv.major) |  | ||||||
| 	if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { |  | ||||||
| 		return v[:j] |  | ||||||
| 	} |  | ||||||
| 	return v[:i] + "." + pv.minor |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Prerelease returns the prerelease suffix of the semantic version v. |  | ||||||
| // For example, Prerelease("v2.1.0-pre+meta") == "-pre". |  | ||||||
| // If v is an invalid semantic version string, Prerelease returns the empty string. |  | ||||||
| func Prerelease(v string) string { |  | ||||||
| 	pv, ok := parse(v) |  | ||||||
| 	if !ok { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return pv.prerelease |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Build returns the build suffix of the semantic version v. |  | ||||||
| // For example, Build("v2.1.0+meta") == "+meta". |  | ||||||
| // If v is an invalid semantic version string, Build returns the empty string. |  | ||||||
| func Build(v string) string { |  | ||||||
| 	pv, ok := parse(v) |  | ||||||
| 	if !ok { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return pv.build |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Compare returns an integer comparing two versions according to |  | ||||||
| // semantic version precedence. |  | ||||||
| // The result will be 0 if v == w, -1 if v < w, or +1 if v > w. |  | ||||||
| // |  | ||||||
| // An invalid semantic version string is considered less than a valid one. |  | ||||||
| // All invalid semantic version strings compare equal to each other. |  | ||||||
| func Compare(v, w string) int { |  | ||||||
| 	pv, ok1 := parse(v) |  | ||||||
| 	pw, ok2 := parse(w) |  | ||||||
| 	if !ok1 && !ok2 { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	if !ok1 { |  | ||||||
| 		return -1 |  | ||||||
| 	} |  | ||||||
| 	if !ok2 { |  | ||||||
| 		return +1 |  | ||||||
| 	} |  | ||||||
| 	if c := compareInt(pv.major, pw.major); c != 0 { |  | ||||||
| 		return c |  | ||||||
| 	} |  | ||||||
| 	if c := compareInt(pv.minor, pw.minor); c != 0 { |  | ||||||
| 		return c |  | ||||||
| 	} |  | ||||||
| 	if c := compareInt(pv.patch, pw.patch); c != 0 { |  | ||||||
| 		return c |  | ||||||
| 	} |  | ||||||
| 	return comparePrerelease(pv.prerelease, pw.prerelease) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Max canonicalizes its arguments and then returns the version string |  | ||||||
| // that compares greater. |  | ||||||
| func Max(v, w string) string { |  | ||||||
| 	v = Canonical(v) |  | ||||||
| 	w = Canonical(w) |  | ||||||
| 	if Compare(v, w) > 0 { |  | ||||||
| 		return v |  | ||||||
| 	} |  | ||||||
| 	return w |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parse(v string) (p parsed, ok bool) { |  | ||||||
| 	if v == "" || v[0] != 'v' { |  | ||||||
| 		p.err = "missing v prefix" |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.major, v, ok = parseInt(v[1:]) |  | ||||||
| 	if !ok { |  | ||||||
| 		p.err = "bad major version" |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if v == "" { |  | ||||||
| 		p.minor = "0" |  | ||||||
| 		p.patch = "0" |  | ||||||
| 		p.short = ".0.0" |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if v[0] != '.' { |  | ||||||
| 		p.err = "bad minor prefix" |  | ||||||
| 		ok = false |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.minor, v, ok = parseInt(v[1:]) |  | ||||||
| 	if !ok { |  | ||||||
| 		p.err = "bad minor version" |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if v == "" { |  | ||||||
| 		p.patch = "0" |  | ||||||
| 		p.short = ".0" |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if v[0] != '.' { |  | ||||||
| 		p.err = "bad patch prefix" |  | ||||||
| 		ok = false |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	p.patch, v, ok = parseInt(v[1:]) |  | ||||||
| 	if !ok { |  | ||||||
| 		p.err = "bad patch version" |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if len(v) > 0 && v[0] == '-' { |  | ||||||
| 		p.prerelease, v, ok = parsePrerelease(v) |  | ||||||
| 		if !ok { |  | ||||||
| 			p.err = "bad prerelease" |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(v) > 0 && v[0] == '+' { |  | ||||||
| 		p.build, v, ok = parseBuild(v) |  | ||||||
| 		if !ok { |  | ||||||
| 			p.err = "bad build" |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if v != "" { |  | ||||||
| 		p.err = "junk on end" |  | ||||||
| 		ok = false |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ok = true |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseInt(v string) (t, rest string, ok bool) { |  | ||||||
| 	if v == "" { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if v[0] < '0' || '9' < v[0] { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	i := 1 |  | ||||||
| 	for i < len(v) && '0' <= v[i] && v[i] <= '9' { |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	if v[0] == '0' && i != 1 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	return v[:i], v[i:], true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parsePrerelease(v string) (t, rest string, ok bool) { |  | ||||||
| 	// "A pre-release version MAY be denoted by appending a hyphen and |  | ||||||
| 	// a series of dot separated identifiers immediately following the patch version. |  | ||||||
| 	// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. |  | ||||||
| 	// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." |  | ||||||
| 	if v == "" || v[0] != '-' { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	i := 1 |  | ||||||
| 	start := 1 |  | ||||||
| 	for i < len(v) && v[i] != '+' { |  | ||||||
| 		if !isIdentChar(v[i]) && v[i] != '.' { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		if v[i] == '.' { |  | ||||||
| 			if start == i || isBadNum(v[start:i]) { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			start = i + 1 |  | ||||||
| 		} |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	if start == i || isBadNum(v[start:i]) { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	return v[:i], v[i:], true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func parseBuild(v string) (t, rest string, ok bool) { |  | ||||||
| 	if v == "" || v[0] != '+' { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	i := 1 |  | ||||||
| 	start := 1 |  | ||||||
| 	for i < len(v) { |  | ||||||
| 		if !isIdentChar(v[i]) && v[i] != '.' { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		if v[i] == '.' { |  | ||||||
| 			if start == i { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			start = i + 1 |  | ||||||
| 		} |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	if start == i { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	return v[:i], v[i:], true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isIdentChar(c byte) bool { |  | ||||||
| 	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isBadNum(v string) bool { |  | ||||||
| 	i := 0 |  | ||||||
| 	for i < len(v) && '0' <= v[i] && v[i] <= '9' { |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	return i == len(v) && i > 1 && v[0] == '0' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isNum(v string) bool { |  | ||||||
| 	i := 0 |  | ||||||
| 	for i < len(v) && '0' <= v[i] && v[i] <= '9' { |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	return i == len(v) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func compareInt(x, y string) int { |  | ||||||
| 	if x == y { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	if len(x) < len(y) { |  | ||||||
| 		return -1 |  | ||||||
| 	} |  | ||||||
| 	if len(x) > len(y) { |  | ||||||
| 		return +1 |  | ||||||
| 	} |  | ||||||
| 	if x < y { |  | ||||||
| 		return -1 |  | ||||||
| 	} else { |  | ||||||
| 		return +1 |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func comparePrerelease(x, y string) int { |  | ||||||
| 	// "When major, minor, and patch are equal, a pre-release version has |  | ||||||
| 	// lower precedence than a normal version. |  | ||||||
| 	// Example: 1.0.0-alpha < 1.0.0. |  | ||||||
| 	// Precedence for two pre-release versions with the same major, minor, |  | ||||||
| 	// and patch version MUST be determined by comparing each dot separated |  | ||||||
| 	// identifier from left to right until a difference is found as follows: |  | ||||||
| 	// identifiers consisting of only digits are compared numerically and |  | ||||||
| 	// identifiers with letters or hyphens are compared lexically in ASCII |  | ||||||
| 	// sort order. Numeric identifiers always have lower precedence than |  | ||||||
| 	// non-numeric identifiers. A larger set of pre-release fields has a |  | ||||||
| 	// higher precedence than a smaller set, if all of the preceding |  | ||||||
| 	// identifiers are equal. |  | ||||||
| 	// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < |  | ||||||
| 	// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." |  | ||||||
| 	if x == y { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	if x == "" { |  | ||||||
| 		return +1 |  | ||||||
| 	} |  | ||||||
| 	if y == "" { |  | ||||||
| 		return -1 |  | ||||||
| 	} |  | ||||||
| 	for x != "" && y != "" { |  | ||||||
| 		x = x[1:] // skip - or . |  | ||||||
| 		y = y[1:] // skip - or . |  | ||||||
| 		var dx, dy string |  | ||||||
| 		dx, x = nextIdent(x) |  | ||||||
| 		dy, y = nextIdent(y) |  | ||||||
| 		if dx != dy { |  | ||||||
| 			ix := isNum(dx) |  | ||||||
| 			iy := isNum(dy) |  | ||||||
| 			if ix != iy { |  | ||||||
| 				if ix { |  | ||||||
| 					return -1 |  | ||||||
| 				} else { |  | ||||||
| 					return +1 |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if ix { |  | ||||||
| 				if len(dx) < len(dy) { |  | ||||||
| 					return -1 |  | ||||||
| 				} |  | ||||||
| 				if len(dx) > len(dy) { |  | ||||||
| 					return +1 |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if dx < dy { |  | ||||||
| 				return -1 |  | ||||||
| 			} else { |  | ||||||
| 				return +1 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if x == "" { |  | ||||||
| 		return -1 |  | ||||||
| 	} else { |  | ||||||
| 		return +1 |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func nextIdent(x string) (dx, rest string) { |  | ||||||
| 	i := 0 |  | ||||||
| 	for i < len(x) && x[i] != '.' { |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	return x[:i], x[i:] |  | ||||||
| } |  | ||||||
							
								
								
									
										3
									
								
								vendor/golang.org/x/tools/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/golang.org/x/tools/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | |||||||
| # This source code refers to The Go Authors for copyright purposes. |  | ||||||
| # The master list of authors is in the main Go distribution, |  | ||||||
| # visible at http://tip.golang.org/AUTHORS. |  | ||||||
							
								
								
									
										3
									
								
								vendor/golang.org/x/tools/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/golang.org/x/tools/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +0,0 @@ | |||||||
| # This source code was written by the Go contributors. |  | ||||||
| # The master list of contributors is in the main Go distribution, |  | ||||||
| # visible at http://tip.golang.org/CONTRIBUTORS. |  | ||||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/tools/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/tools/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,27 +0,0 @@ | |||||||
| Copyright (c) 2009 The Go Authors. All rights reserved. |  | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/tools/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/tools/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,22 +0,0 @@ | |||||||
| Additional IP Rights Grant (Patents) |  | ||||||
| 
 |  | ||||||
| "This implementation" means the copyrightable works distributed by |  | ||||||
| Google as part of the Go project. |  | ||||||
| 
 |  | ||||||
| Google hereby grants to You a perpetual, worldwide, non-exclusive, |  | ||||||
| no-charge, royalty-free, irrevocable (except as stated in this section) |  | ||||||
| patent license to make, have made, use, offer to sell, sell, import, |  | ||||||
| transfer and otherwise run, modify and propagate the contents of this |  | ||||||
| implementation of Go, where such license applies only to those patent |  | ||||||
| claims, both currently owned or controlled by Google and acquired in |  | ||||||
| the future, licensable by Google that are necessarily infringed by this |  | ||||||
| implementation of Go.  This grant does not include claims that would be |  | ||||||
| infringed only as a consequence of further modification of this |  | ||||||
| implementation.  If you or your agent or exclusive licensee institute or |  | ||||||
| order or agree to the institution of patent litigation against any |  | ||||||
| entity (including a cross-claim or counterclaim in a lawsuit) alleging |  | ||||||
| that this implementation of Go or any code incorporated within this |  | ||||||
| implementation of Go constitutes direct or contributory patent |  | ||||||
| infringement, or inducement of patent infringement, then any patent |  | ||||||
| rights granted to you under this License for this implementation of Go |  | ||||||
| shall terminate as of the date such litigation is filed. |  | ||||||
							
								
								
									
										655
									
								
								vendor/golang.org/x/tools/cmd/stringer/stringer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										655
									
								
								vendor/golang.org/x/tools/cmd/stringer/stringer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,655 +0,0 @@ | |||||||
| // Copyright 2014 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer |  | ||||||
| // interface. Given the name of a (signed or unsigned) integer type T that has constants |  | ||||||
| // defined, stringer will create a new self-contained Go source file implementing |  | ||||||
| //	func (t T) String() string |  | ||||||
| // The file is created in the same package and directory as the package that defines T. |  | ||||||
| // It has helpful defaults designed for use with go generate. |  | ||||||
| // |  | ||||||
| // Stringer works best with constants that are consecutive values such as created using iota, |  | ||||||
| // but creates good code regardless. In the future it might also provide custom support for |  | ||||||
| // constant sets that are bit patterns. |  | ||||||
| // |  | ||||||
| // For example, given this snippet, |  | ||||||
| // |  | ||||||
| //	package painkiller |  | ||||||
| // |  | ||||||
| //	type Pill int |  | ||||||
| // |  | ||||||
| //	const ( |  | ||||||
| //		Placebo Pill = iota |  | ||||||
| //		Aspirin |  | ||||||
| //		Ibuprofen |  | ||||||
| //		Paracetamol |  | ||||||
| //		Acetaminophen = Paracetamol |  | ||||||
| //	) |  | ||||||
| // |  | ||||||
| // running this command |  | ||||||
| // |  | ||||||
| //	stringer -type=Pill |  | ||||||
| // |  | ||||||
| // in the same directory will create the file pill_string.go, in package painkiller, |  | ||||||
| // containing a definition of |  | ||||||
| // |  | ||||||
| //	func (Pill) String() string |  | ||||||
| // |  | ||||||
| // That method will translate the value of a Pill constant to the string representation |  | ||||||
| // of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will |  | ||||||
| // print the string "Aspirin". |  | ||||||
| // |  | ||||||
| // Typically this process would be run using go generate, like this: |  | ||||||
| // |  | ||||||
| //	//go:generate stringer -type=Pill |  | ||||||
| // |  | ||||||
| // If multiple constants have the same value, the lexically first matching name will |  | ||||||
| // be used (in the example, Acetaminophen will print as "Paracetamol"). |  | ||||||
| // |  | ||||||
| // With no arguments, it processes the package in the current directory. |  | ||||||
| // Otherwise, the arguments must name a single directory holding a Go package |  | ||||||
| // or a set of Go source files that represent a single Go package. |  | ||||||
| // |  | ||||||
| // The -type flag accepts a comma-separated list of types so a single run can |  | ||||||
| // generate methods for multiple types. The default output file is t_string.go, |  | ||||||
| // where t is the lower-cased name of the first type listed. It can be overridden |  | ||||||
| // with the -output flag. |  | ||||||
| // |  | ||||||
| // The -linecomment flag tells stringer to generate the text of any line comment, trimmed |  | ||||||
| // of leading spaces, instead of the constant name. For instance, if the constants above had a |  | ||||||
| // Pill prefix, one could write |  | ||||||
| // |  | ||||||
| //	PillAspirin // Aspirin |  | ||||||
| // |  | ||||||
| // to suppress it in the output. |  | ||||||
| package main // import "golang.org/x/tools/cmd/stringer" |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"flag" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/ast" |  | ||||||
| 	"go/constant" |  | ||||||
| 	"go/format" |  | ||||||
| 	"go/token" |  | ||||||
| 	"go/types" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"log" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"sort" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/go/packages" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	typeNames   = flag.String("type", "", "comma-separated list of type names; must be set") |  | ||||||
| 	output      = flag.String("output", "", "output file name; default srcdir/<type>_string.go") |  | ||||||
| 	trimprefix  = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") |  | ||||||
| 	linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") |  | ||||||
| 	buildTags   = flag.String("tags", "", "comma-separated list of build tags to apply") |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Usage is a replacement usage function for the flags package. |  | ||||||
| func Usage() { |  | ||||||
| 	fmt.Fprintf(os.Stderr, "Usage of stringer:\n") |  | ||||||
| 	fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n") |  | ||||||
| 	fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n") |  | ||||||
| 	fmt.Fprintf(os.Stderr, "For more information, see:\n") |  | ||||||
| 	fmt.Fprintf(os.Stderr, "\thttp://godoc.org/golang.org/x/tools/cmd/stringer\n") |  | ||||||
| 	fmt.Fprintf(os.Stderr, "Flags:\n") |  | ||||||
| 	flag.PrintDefaults() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func main() { |  | ||||||
| 	log.SetFlags(0) |  | ||||||
| 	log.SetPrefix("stringer: ") |  | ||||||
| 	flag.Usage = Usage |  | ||||||
| 	flag.Parse() |  | ||||||
| 	if len(*typeNames) == 0 { |  | ||||||
| 		flag.Usage() |  | ||||||
| 		os.Exit(2) |  | ||||||
| 	} |  | ||||||
| 	types := strings.Split(*typeNames, ",") |  | ||||||
| 	var tags []string |  | ||||||
| 	if len(*buildTags) > 0 { |  | ||||||
| 		tags = strings.Split(*buildTags, ",") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// We accept either one directory or a list of files. Which do we have? |  | ||||||
| 	args := flag.Args() |  | ||||||
| 	if len(args) == 0 { |  | ||||||
| 		// Default: process whole package in current directory. |  | ||||||
| 		args = []string{"."} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Parse the package once. |  | ||||||
| 	var dir string |  | ||||||
| 	g := Generator{ |  | ||||||
| 		trimPrefix:  *trimprefix, |  | ||||||
| 		lineComment: *linecomment, |  | ||||||
| 	} |  | ||||||
| 	// TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc). |  | ||||||
| 	if len(args) == 1 && isDirectory(args[0]) { |  | ||||||
| 		dir = args[0] |  | ||||||
| 	} else { |  | ||||||
| 		if len(tags) != 0 { |  | ||||||
| 			log.Fatal("-tags option applies only to directories, not when files are specified") |  | ||||||
| 		} |  | ||||||
| 		dir = filepath.Dir(args[0]) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	g.parsePackage(args, tags) |  | ||||||
| 
 |  | ||||||
| 	// Print the header and package clause. |  | ||||||
| 	g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) |  | ||||||
| 	g.Printf("\n") |  | ||||||
| 	g.Printf("package %s", g.pkg.name) |  | ||||||
| 	g.Printf("\n") |  | ||||||
| 	g.Printf("import \"strconv\"\n") // Used by all methods. |  | ||||||
| 
 |  | ||||||
| 	// Run generate for each type. |  | ||||||
| 	for _, typeName := range types { |  | ||||||
| 		g.generate(typeName) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Format the output. |  | ||||||
| 	src := g.format() |  | ||||||
| 
 |  | ||||||
| 	// Write to file. |  | ||||||
| 	outputName := *output |  | ||||||
| 	if outputName == "" { |  | ||||||
| 		baseName := fmt.Sprintf("%s_string.go", types[0]) |  | ||||||
| 		outputName = filepath.Join(dir, strings.ToLower(baseName)) |  | ||||||
| 	} |  | ||||||
| 	err := ioutil.WriteFile(outputName, src, 0644) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalf("writing output: %s", err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // isDirectory reports whether the named file is a directory. |  | ||||||
| func isDirectory(name string) bool { |  | ||||||
| 	info, err := os.Stat(name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	return info.IsDir() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Generator holds the state of the analysis. Primarily used to buffer |  | ||||||
| // the output for format.Source. |  | ||||||
| type Generator struct { |  | ||||||
| 	buf bytes.Buffer // Accumulated output. |  | ||||||
| 	pkg *Package     // Package we are scanning. |  | ||||||
| 
 |  | ||||||
| 	trimPrefix  string |  | ||||||
| 	lineComment bool |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (g *Generator) Printf(format string, args ...interface{}) { |  | ||||||
| 	fmt.Fprintf(&g.buf, format, args...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // File holds a single parsed file and associated data. |  | ||||||
| type File struct { |  | ||||||
| 	pkg  *Package  // Package to which this file belongs. |  | ||||||
| 	file *ast.File // Parsed AST. |  | ||||||
| 	// These fields are reset for each type being generated. |  | ||||||
| 	typeName string  // Name of the constant type. |  | ||||||
| 	values   []Value // Accumulator for constant values of that type. |  | ||||||
| 
 |  | ||||||
| 	trimPrefix  string |  | ||||||
| 	lineComment bool |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Package struct { |  | ||||||
| 	name  string |  | ||||||
| 	defs  map[*ast.Ident]types.Object |  | ||||||
| 	files []*File |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // parsePackage analyzes the single package constructed from the patterns and tags. |  | ||||||
| // parsePackage exits if there is an error. |  | ||||||
| func (g *Generator) parsePackage(patterns []string, tags []string) { |  | ||||||
| 	cfg := &packages.Config{ |  | ||||||
| 		Mode: packages.LoadSyntax, |  | ||||||
| 		// TODO: Need to think about constants in test files. Maybe write type_string_test.go |  | ||||||
| 		// in a separate pass? For later. |  | ||||||
| 		Tests:      false, |  | ||||||
| 		BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, |  | ||||||
| 	} |  | ||||||
| 	pkgs, err := packages.Load(cfg, patterns...) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	if len(pkgs) != 1 { |  | ||||||
| 		log.Fatalf("error: %d packages found", len(pkgs)) |  | ||||||
| 	} |  | ||||||
| 	g.addPackage(pkgs[0]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // addPackage adds a type checked Package and its syntax files to the generator. |  | ||||||
| func (g *Generator) addPackage(pkg *packages.Package) { |  | ||||||
| 	g.pkg = &Package{ |  | ||||||
| 		name:  pkg.Name, |  | ||||||
| 		defs:  pkg.TypesInfo.Defs, |  | ||||||
| 		files: make([]*File, len(pkg.Syntax)), |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i, file := range pkg.Syntax { |  | ||||||
| 		g.pkg.files[i] = &File{ |  | ||||||
| 			file:        file, |  | ||||||
| 			pkg:         g.pkg, |  | ||||||
| 			trimPrefix:  g.trimPrefix, |  | ||||||
| 			lineComment: g.lineComment, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // generate produces the String method for the named type. |  | ||||||
| func (g *Generator) generate(typeName string) { |  | ||||||
| 	values := make([]Value, 0, 100) |  | ||||||
| 	for _, file := range g.pkg.files { |  | ||||||
| 		// Set the state for this run of the walker. |  | ||||||
| 		file.typeName = typeName |  | ||||||
| 		file.values = nil |  | ||||||
| 		if file.file != nil { |  | ||||||
| 			ast.Inspect(file.file, file.genDecl) |  | ||||||
| 			values = append(values, file.values...) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if len(values) == 0 { |  | ||||||
| 		log.Fatalf("no values defined for type %s", typeName) |  | ||||||
| 	} |  | ||||||
| 	// Generate code that will fail if the constants change value. |  | ||||||
| 	g.Printf("func _() {\n") |  | ||||||
| 	g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n") |  | ||||||
| 	g.Printf("\t// Re-run the stringer command to generate them again.\n") |  | ||||||
| 	g.Printf("\tvar x [1]struct{}\n") |  | ||||||
| 	for _, v := range values { |  | ||||||
| 		g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str) |  | ||||||
| 	} |  | ||||||
| 	g.Printf("}\n") |  | ||||||
| 	runs := splitIntoRuns(values) |  | ||||||
| 	// The decision of which pattern to use depends on the number of |  | ||||||
| 	// runs in the numbers. If there's only one, it's easy. For more than |  | ||||||
| 	// one, there's a tradeoff between complexity and size of the data |  | ||||||
| 	// and code vs. the simplicity of a map. A map takes more space, |  | ||||||
| 	// but so does the code. The decision here (crossover at 10) is |  | ||||||
| 	// arbitrary, but considers that for large numbers of runs the cost |  | ||||||
| 	// of the linear scan in the switch might become important, and |  | ||||||
| 	// rather than use yet another algorithm such as binary search, |  | ||||||
| 	// we punt and use a map. In any case, the likelihood of a map |  | ||||||
| 	// being necessary for any realistic example other than bitmasks |  | ||||||
| 	// is very low. And bitmasks probably deserve their own analysis, |  | ||||||
| 	// to be done some other day. |  | ||||||
| 	switch { |  | ||||||
| 	case len(runs) == 1: |  | ||||||
| 		g.buildOneRun(runs, typeName) |  | ||||||
| 	case len(runs) <= 10: |  | ||||||
| 		g.buildMultipleRuns(runs, typeName) |  | ||||||
| 	default: |  | ||||||
| 		g.buildMap(runs, typeName) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // splitIntoRuns breaks the values into runs of contiguous sequences. |  | ||||||
| // For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}. |  | ||||||
| // The input slice is known to be non-empty. |  | ||||||
| func splitIntoRuns(values []Value) [][]Value { |  | ||||||
| 	// We use stable sort so the lexically first name is chosen for equal elements. |  | ||||||
| 	sort.Stable(byValue(values)) |  | ||||||
| 	// Remove duplicates. Stable sort has put the one we want to print first, |  | ||||||
| 	// so use that one. The String method won't care about which named constant |  | ||||||
| 	// was the argument, so the first name for the given value is the only one to keep. |  | ||||||
| 	// We need to do this because identical values would cause the switch or map |  | ||||||
| 	// to fail to compile. |  | ||||||
| 	j := 1 |  | ||||||
| 	for i := 1; i < len(values); i++ { |  | ||||||
| 		if values[i].value != values[i-1].value { |  | ||||||
| 			values[j] = values[i] |  | ||||||
| 			j++ |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	values = values[:j] |  | ||||||
| 	runs := make([][]Value, 0, 10) |  | ||||||
| 	for len(values) > 0 { |  | ||||||
| 		// One contiguous sequence per outer loop. |  | ||||||
| 		i := 1 |  | ||||||
| 		for i < len(values) && values[i].value == values[i-1].value+1 { |  | ||||||
| 			i++ |  | ||||||
| 		} |  | ||||||
| 		runs = append(runs, values[:i]) |  | ||||||
| 		values = values[i:] |  | ||||||
| 	} |  | ||||||
| 	return runs |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // format returns the gofmt-ed contents of the Generator's buffer. |  | ||||||
| func (g *Generator) format() []byte { |  | ||||||
| 	src, err := format.Source(g.buf.Bytes()) |  | ||||||
| 	if err != nil { |  | ||||||
| 		// Should never happen, but can arise when developing this code. |  | ||||||
| 		// The user can compile the output to see the error. |  | ||||||
| 		log.Printf("warning: internal error: invalid Go generated: %s", err) |  | ||||||
| 		log.Printf("warning: compile the package to analyze the error") |  | ||||||
| 		return g.buf.Bytes() |  | ||||||
| 	} |  | ||||||
| 	return src |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Value represents a declared constant. |  | ||||||
| type Value struct { |  | ||||||
| 	originalName string // The name of the constant. |  | ||||||
| 	name         string // The name with trimmed prefix. |  | ||||||
| 	// The value is stored as a bit pattern alone. The boolean tells us |  | ||||||
| 	// whether to interpret it as an int64 or a uint64; the only place |  | ||||||
| 	// this matters is when sorting. |  | ||||||
| 	// Much of the time the str field is all we need; it is printed |  | ||||||
| 	// by Value.String. |  | ||||||
| 	value  uint64 // Will be converted to int64 when needed. |  | ||||||
| 	signed bool   // Whether the constant is a signed type. |  | ||||||
| 	str    string // The string representation given by the "go/constant" package. |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (v *Value) String() string { |  | ||||||
| 	return v.str |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // byValue lets us sort the constants into increasing order. |  | ||||||
| // We take care in the Less method to sort in signed or unsigned order, |  | ||||||
| // as appropriate. |  | ||||||
| type byValue []Value |  | ||||||
| 
 |  | ||||||
| func (b byValue) Len() int      { return len(b) } |  | ||||||
| func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] } |  | ||||||
| func (b byValue) Less(i, j int) bool { |  | ||||||
| 	if b[i].signed { |  | ||||||
| 		return int64(b[i].value) < int64(b[j].value) |  | ||||||
| 	} |  | ||||||
| 	return b[i].value < b[j].value |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // genDecl processes one declaration clause. |  | ||||||
| func (f *File) genDecl(node ast.Node) bool { |  | ||||||
| 	decl, ok := node.(*ast.GenDecl) |  | ||||||
| 	if !ok || decl.Tok != token.CONST { |  | ||||||
| 		// We only care about const declarations. |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	// The name of the type of the constants we are declaring. |  | ||||||
| 	// Can change if this is a multi-element declaration. |  | ||||||
| 	typ := "" |  | ||||||
| 	// Loop over the elements of the declaration. Each element is a ValueSpec: |  | ||||||
| 	// a list of names possibly followed by a type, possibly followed by values. |  | ||||||
| 	// If the type and value are both missing, we carry down the type (and value, |  | ||||||
| 	// but the "go/types" package takes care of that). |  | ||||||
| 	for _, spec := range decl.Specs { |  | ||||||
| 		vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. |  | ||||||
| 		if vspec.Type == nil && len(vspec.Values) > 0 { |  | ||||||
| 			// "X = 1". With no type but a value. If the constant is untyped, |  | ||||||
| 			// skip this vspec and reset the remembered type. |  | ||||||
| 			typ = "" |  | ||||||
| 
 |  | ||||||
| 			// If this is a simple type conversion, remember the type. |  | ||||||
| 			// We don't mind if this is actually a call; a qualified call won't |  | ||||||
| 			// be matched (that will be SelectorExpr, not Ident), and only unusual |  | ||||||
| 			// situations will result in a function call that appears to be |  | ||||||
| 			// a type conversion. |  | ||||||
| 			ce, ok := vspec.Values[0].(*ast.CallExpr) |  | ||||||
| 			if !ok { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			id, ok := ce.Fun.(*ast.Ident) |  | ||||||
| 			if !ok { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			typ = id.Name |  | ||||||
| 		} |  | ||||||
| 		if vspec.Type != nil { |  | ||||||
| 			// "X T". We have a type. Remember it. |  | ||||||
| 			ident, ok := vspec.Type.(*ast.Ident) |  | ||||||
| 			if !ok { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			typ = ident.Name |  | ||||||
| 		} |  | ||||||
| 		if typ != f.typeName { |  | ||||||
| 			// This is not the type we're looking for. |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		// We now have a list of names (from one line of source code) all being |  | ||||||
| 		// declared with the desired type. |  | ||||||
| 		// Grab their names and actual values and store them in f.values. |  | ||||||
| 		for _, name := range vspec.Names { |  | ||||||
| 			if name.Name == "_" { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			// This dance lets the type checker find the values for us. It's a |  | ||||||
| 			// bit tricky: look up the object declared by the name, find its |  | ||||||
| 			// types.Const, and extract its value. |  | ||||||
| 			obj, ok := f.pkg.defs[name] |  | ||||||
| 			if !ok { |  | ||||||
| 				log.Fatalf("no value for constant %s", name) |  | ||||||
| 			} |  | ||||||
| 			info := obj.Type().Underlying().(*types.Basic).Info() |  | ||||||
| 			if info&types.IsInteger == 0 { |  | ||||||
| 				log.Fatalf("can't handle non-integer constant type %s", typ) |  | ||||||
| 			} |  | ||||||
| 			value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. |  | ||||||
| 			if value.Kind() != constant.Int { |  | ||||||
| 				log.Fatalf("can't happen: constant is not an integer %s", name) |  | ||||||
| 			} |  | ||||||
| 			i64, isInt := constant.Int64Val(value) |  | ||||||
| 			u64, isUint := constant.Uint64Val(value) |  | ||||||
| 			if !isInt && !isUint { |  | ||||||
| 				log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) |  | ||||||
| 			} |  | ||||||
| 			if !isInt { |  | ||||||
| 				u64 = uint64(i64) |  | ||||||
| 			} |  | ||||||
| 			v := Value{ |  | ||||||
| 				originalName: name.Name, |  | ||||||
| 				value:        u64, |  | ||||||
| 				signed:       info&types.IsUnsigned == 0, |  | ||||||
| 				str:          value.String(), |  | ||||||
| 			} |  | ||||||
| 			if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 { |  | ||||||
| 				v.name = strings.TrimSpace(c.Text()) |  | ||||||
| 			} else { |  | ||||||
| 				v.name = strings.TrimPrefix(v.originalName, f.trimPrefix) |  | ||||||
| 			} |  | ||||||
| 			f.values = append(f.values, v) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Helpers |  | ||||||
| 
 |  | ||||||
| // usize returns the number of bits of the smallest unsigned integer |  | ||||||
| // type that will hold n. Used to create the smallest possible slice of |  | ||||||
| // integers to use as indexes into the concatenated strings. |  | ||||||
| func usize(n int) int { |  | ||||||
| 	switch { |  | ||||||
| 	case n < 1<<8: |  | ||||||
| 		return 8 |  | ||||||
| 	case n < 1<<16: |  | ||||||
| 		return 16 |  | ||||||
| 	default: |  | ||||||
| 		// 2^32 is enough constants for anyone. |  | ||||||
| 		return 32 |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // declareIndexAndNameVars declares the index slices and concatenated names |  | ||||||
| // strings representing the runs of values. |  | ||||||
| func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { |  | ||||||
| 	var indexes, names []string |  | ||||||
| 	for i, run := range runs { |  | ||||||
| 		index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) |  | ||||||
| 		if len(run) != 1 { |  | ||||||
| 			indexes = append(indexes, index) |  | ||||||
| 		} |  | ||||||
| 		names = append(names, name) |  | ||||||
| 	} |  | ||||||
| 	g.Printf("const (\n") |  | ||||||
| 	for _, name := range names { |  | ||||||
| 		g.Printf("\t%s\n", name) |  | ||||||
| 	} |  | ||||||
| 	g.Printf(")\n\n") |  | ||||||
| 
 |  | ||||||
| 	if len(indexes) > 0 { |  | ||||||
| 		g.Printf("var (") |  | ||||||
| 		for _, index := range indexes { |  | ||||||
| 			g.Printf("\t%s\n", index) |  | ||||||
| 		} |  | ||||||
| 		g.Printf(")\n\n") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // declareIndexAndNameVar is the single-run version of declareIndexAndNameVars |  | ||||||
| func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) { |  | ||||||
| 	index, name := g.createIndexAndNameDecl(run, typeName, "") |  | ||||||
| 	g.Printf("const %s\n", name) |  | ||||||
| 	g.Printf("var %s\n", index) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var". |  | ||||||
| func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) { |  | ||||||
| 	b := new(bytes.Buffer) |  | ||||||
| 	indexes := make([]int, len(run)) |  | ||||||
| 	for i := range run { |  | ||||||
| 		b.WriteString(run[i].name) |  | ||||||
| 		indexes[i] = b.Len() |  | ||||||
| 	} |  | ||||||
| 	nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String()) |  | ||||||
| 	nameLen := b.Len() |  | ||||||
| 	b.Reset() |  | ||||||
| 	fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen)) |  | ||||||
| 	for i, v := range indexes { |  | ||||||
| 		if i > 0 { |  | ||||||
| 			fmt.Fprintf(b, ", ") |  | ||||||
| 		} |  | ||||||
| 		fmt.Fprintf(b, "%d", v) |  | ||||||
| 	} |  | ||||||
| 	fmt.Fprintf(b, "}") |  | ||||||
| 	return b.String(), nameConst |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // declareNameVars declares the concatenated names string representing all the values in the runs. |  | ||||||
| func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) { |  | ||||||
| 	g.Printf("const _%s_name%s = \"", typeName, suffix) |  | ||||||
| 	for _, run := range runs { |  | ||||||
| 		for i := range run { |  | ||||||
| 			g.Printf("%s", run[i].name) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	g.Printf("\"\n") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // buildOneRun generates the variables and String method for a single run of contiguous values. |  | ||||||
| func (g *Generator) buildOneRun(runs [][]Value, typeName string) { |  | ||||||
| 	values := runs[0] |  | ||||||
| 	g.Printf("\n") |  | ||||||
| 	g.declareIndexAndNameVar(values, typeName) |  | ||||||
| 	// The generated code is simple enough to write as a Printf format. |  | ||||||
| 	lessThanZero := "" |  | ||||||
| 	if values[0].signed { |  | ||||||
| 		lessThanZero = "i < 0 || " |  | ||||||
| 	} |  | ||||||
| 	if values[0].value == 0 { // Signed or unsigned, 0 is still 0. |  | ||||||
| 		g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero) |  | ||||||
| 	} else { |  | ||||||
| 		g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Arguments to format are: |  | ||||||
| //	[1]: type name |  | ||||||
| //	[2]: size of index element (8 for uint8 etc.) |  | ||||||
| //	[3]: less than zero check (for signed types) |  | ||||||
| const stringOneRun = `func (i %[1]s) String() string { |  | ||||||
| 	if %[3]si >= %[1]s(len(_%[1]s_index)-1) { |  | ||||||
| 		return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" |  | ||||||
| 	} |  | ||||||
| 	return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] |  | ||||||
| } |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| // Arguments to format are: |  | ||||||
| //	[1]: type name |  | ||||||
| //	[2]: lowest defined value for type, as a string |  | ||||||
| //	[3]: size of index element (8 for uint8 etc.) |  | ||||||
| //	[4]: less than zero check (for signed types) |  | ||||||
| /* |  | ||||||
|  */ |  | ||||||
| const stringOneRunWithOffset = `func (i %[1]s) String() string { |  | ||||||
| 	i -= %[2]s |  | ||||||
| 	if %[4]si >= %[1]s(len(_%[1]s_index)-1) { |  | ||||||
| 		return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")" |  | ||||||
| 	} |  | ||||||
| 	return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]] |  | ||||||
| } |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values. |  | ||||||
| // For this pattern, a single Printf format won't do. |  | ||||||
| func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { |  | ||||||
| 	g.Printf("\n") |  | ||||||
| 	g.declareIndexAndNameVars(runs, typeName) |  | ||||||
| 	g.Printf("func (i %s) String() string {\n", typeName) |  | ||||||
| 	g.Printf("\tswitch {\n") |  | ||||||
| 	for i, values := range runs { |  | ||||||
| 		if len(values) == 1 { |  | ||||||
| 			g.Printf("\tcase i == %s:\n", &values[0]) |  | ||||||
| 			g.Printf("\t\treturn _%s_name_%d\n", typeName, i) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if values[0].value == 0 && !values[0].signed { |  | ||||||
| 			// For an unsigned lower bound of 0, "0 <= i" would be redundant. |  | ||||||
| 			g.Printf("\tcase i <= %s:\n", &values[len(values)-1]) |  | ||||||
| 		} else { |  | ||||||
| 			g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1]) |  | ||||||
| 		} |  | ||||||
| 		if values[0].value != 0 { |  | ||||||
| 			g.Printf("\t\ti -= %s\n", &values[0]) |  | ||||||
| 		} |  | ||||||
| 		g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n", |  | ||||||
| 			typeName, i, typeName, i, typeName, i) |  | ||||||
| 	} |  | ||||||
| 	g.Printf("\tdefault:\n") |  | ||||||
| 	g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName) |  | ||||||
| 	g.Printf("\t}\n") |  | ||||||
| 	g.Printf("}\n") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // buildMap handles the case where the space is so sparse a map is a reasonable fallback. |  | ||||||
| // It's a rare situation but has simple code. |  | ||||||
| func (g *Generator) buildMap(runs [][]Value, typeName string) { |  | ||||||
| 	g.Printf("\n") |  | ||||||
| 	g.declareNameVars(runs, typeName, "") |  | ||||||
| 	g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName) |  | ||||||
| 	n := 0 |  | ||||||
| 	for _, values := range runs { |  | ||||||
| 		for _, value := range values { |  | ||||||
| 			g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) |  | ||||||
| 			n += len(value.name) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	g.Printf("}\n\n") |  | ||||||
| 	g.Printf(stringMap, typeName) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Argument to format is the type name. |  | ||||||
| const stringMap = `func (i %[1]s) String() string { |  | ||||||
| 	if str, ok := _%[1]s_map[i]; ok { |  | ||||||
| 		return str |  | ||||||
| 	} |  | ||||||
| 	return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" |  | ||||||
| } |  | ||||||
| ` |  | ||||||
							
								
								
									
										109
									
								
								vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,109 +0,0 @@ | |||||||
| // Copyright 2016 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package gcexportdata provides functions for locating, reading, and |  | ||||||
| // writing export data files containing type information produced by the |  | ||||||
| // gc compiler.  This package supports go1.7 export data format and all |  | ||||||
| // later versions. |  | ||||||
| // |  | ||||||
| // Although it might seem convenient for this package to live alongside |  | ||||||
| // go/types in the standard library, this would cause version skew |  | ||||||
| // problems for developer tools that use it, since they must be able to |  | ||||||
| // consume the outputs of the gc compiler both before and after a Go |  | ||||||
| // update such as from Go 1.7 to Go 1.8.  Because this package lives in |  | ||||||
| // golang.org/x/tools, sites can update their version of this repo some |  | ||||||
| // time before the Go 1.8 release and rebuild and redeploy their |  | ||||||
| // developer tools, which will then be able to consume both Go 1.7 and |  | ||||||
| // Go 1.8 export data files, so they will work before and after the |  | ||||||
| // Go update. (See discussion at https://golang.org/issue/15651.) |  | ||||||
| // |  | ||||||
| package gcexportdata // import "golang.org/x/tools/go/gcexportdata" |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bufio" |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/token" |  | ||||||
| 	"go/types" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/go/internal/gcimporter" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Find returns the name of an object (.o) or archive (.a) file |  | ||||||
| // containing type information for the specified import path, |  | ||||||
| // using the workspace layout conventions of go/build. |  | ||||||
| // If no file was found, an empty filename is returned. |  | ||||||
| // |  | ||||||
| // A relative srcDir is interpreted relative to the current working directory. |  | ||||||
| // |  | ||||||
| // Find also returns the package's resolved (canonical) import path, |  | ||||||
| // reflecting the effects of srcDir and vendoring on importPath. |  | ||||||
| func Find(importPath, srcDir string) (filename, path string) { |  | ||||||
| 	return gcimporter.FindPkg(importPath, srcDir) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewReader returns a reader for the export data section of an object |  | ||||||
| // (.o) or archive (.a) file read from r.  The new reader may provide |  | ||||||
| // additional trailing data beyond the end of the export data. |  | ||||||
| func NewReader(r io.Reader) (io.Reader, error) { |  | ||||||
| 	buf := bufio.NewReader(r) |  | ||||||
| 	_, err := gcimporter.FindExportData(buf) |  | ||||||
| 	// If we ever switch to a zip-like archive format with the ToC |  | ||||||
| 	// at the end, we can return the correct portion of export data, |  | ||||||
| 	// but for now we must return the entire rest of the file. |  | ||||||
| 	return buf, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Read reads export data from in, decodes it, and returns type |  | ||||||
| // information for the package. |  | ||||||
| // The package name is specified by path. |  | ||||||
| // File position information is added to fset. |  | ||||||
| // |  | ||||||
| // Read may inspect and add to the imports map to ensure that references |  | ||||||
| // within the export data to other packages are consistent.  The caller |  | ||||||
| // must ensure that imports[path] does not exist, or exists but is |  | ||||||
| // incomplete (see types.Package.Complete), and Read inserts the |  | ||||||
| // resulting package into this map entry. |  | ||||||
| // |  | ||||||
| // On return, the state of the reader is undefined. |  | ||||||
| func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) { |  | ||||||
| 	data, err := ioutil.ReadAll(in) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("reading export data for %q: %v", path, err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if bytes.HasPrefix(data, []byte("!<arch>")) { |  | ||||||
| 		return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// The App Engine Go runtime v1.6 uses the old export data format. |  | ||||||
| 	// TODO(adonovan): delete once v1.7 has been around for a while. |  | ||||||
| 	if bytes.HasPrefix(data, []byte("package ")) { |  | ||||||
| 		return gcimporter.ImportData(imports, path, path, bytes.NewReader(data)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// The indexed export format starts with an 'i'; the older |  | ||||||
| 	// binary export format starts with a 'c', 'd', or 'v' |  | ||||||
| 	// (from "version"). Select appropriate importer. |  | ||||||
| 	if len(data) > 0 && data[0] == 'i' { |  | ||||||
| 		_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) |  | ||||||
| 		return pkg, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	_, pkg, err := gcimporter.BImportData(fset, imports, data, path) |  | ||||||
| 	return pkg, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Write writes encoded type information for the specified package to out. |  | ||||||
| // The FileSet provides file position information for named objects. |  | ||||||
| func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error { |  | ||||||
| 	b, err := gcimporter.IExportData(fset, pkg) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	_, err = out.Write(b) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
							
								
								
									
										73
									
								
								vendor/golang.org/x/tools/go/gcexportdata/importer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										73
									
								
								vendor/golang.org/x/tools/go/gcexportdata/importer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,73 +0,0 @@ | |||||||
| // Copyright 2016 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package gcexportdata |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/token" |  | ||||||
| 	"go/types" |  | ||||||
| 	"os" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // NewImporter returns a new instance of the types.Importer interface |  | ||||||
| // that reads type information from export data files written by gc. |  | ||||||
| // The Importer also satisfies types.ImporterFrom. |  | ||||||
| // |  | ||||||
| // Export data files are located using "go build" workspace conventions |  | ||||||
| // and the build.Default context. |  | ||||||
| // |  | ||||||
| // Use this importer instead of go/importer.For("gc", ...) to avoid the |  | ||||||
| // version-skew problems described in the documentation of this package, |  | ||||||
| // or to control the FileSet or access the imports map populated during |  | ||||||
| // package loading. |  | ||||||
| // |  | ||||||
| func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom { |  | ||||||
| 	return importer{fset, imports} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type importer struct { |  | ||||||
| 	fset    *token.FileSet |  | ||||||
| 	imports map[string]*types.Package |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (imp importer) Import(importPath string) (*types.Package, error) { |  | ||||||
| 	return imp.ImportFrom(importPath, "", 0) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) { |  | ||||||
| 	filename, path := Find(importPath, srcDir) |  | ||||||
| 	if filename == "" { |  | ||||||
| 		if importPath == "unsafe" { |  | ||||||
| 			// Even for unsafe, call Find first in case |  | ||||||
| 			// the package was vendored. |  | ||||||
| 			return types.Unsafe, nil |  | ||||||
| 		} |  | ||||||
| 		return nil, fmt.Errorf("can't find import: %s", importPath) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if pkg, ok := imp.imports[path]; ok && pkg.Complete() { |  | ||||||
| 		return pkg, nil // cache hit |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// open file |  | ||||||
| 	f, err := os.Open(filename) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	defer func() { |  | ||||||
| 		f.Close() |  | ||||||
| 		if err != nil { |  | ||||||
| 			// add file name to error |  | ||||||
| 			err = fmt.Errorf("reading export data: %s: %v", filename, err) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	r, err := NewReader(f) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return Read(r, imp.fset, imp.imports, path) |  | ||||||
| } |  | ||||||
							
								
								
									
										852
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										852
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,852 +0,0 @@ | |||||||
| // Copyright 2016 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Binary package export. |  | ||||||
| // This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; |  | ||||||
| // see that file for specification of the format. |  | ||||||
| 
 |  | ||||||
| package gcimporter |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/binary" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/ast" |  | ||||||
| 	"go/constant" |  | ||||||
| 	"go/token" |  | ||||||
| 	"go/types" |  | ||||||
| 	"math" |  | ||||||
| 	"math/big" |  | ||||||
| 	"sort" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // If debugFormat is set, each integer and string value is preceded by a marker |  | ||||||
| // and position information in the encoding. This mechanism permits an importer |  | ||||||
| // to recognize immediately when it is out of sync. The importer recognizes this |  | ||||||
| // mode automatically (i.e., it can import export data produced with debugging |  | ||||||
| // support even if debugFormat is not set at the time of import). This mode will |  | ||||||
| // lead to massively larger export data (by a factor of 2 to 3) and should only |  | ||||||
| // be enabled during development and debugging. |  | ||||||
| // |  | ||||||
| // NOTE: This flag is the first flag to enable if importing dies because of |  | ||||||
| // (suspected) format errors, and whenever a change is made to the format. |  | ||||||
| const debugFormat = false // default: false |  | ||||||
| 
 |  | ||||||
| // If trace is set, debugging output is printed to std out. |  | ||||||
| const trace = false // default: false |  | ||||||
| 
 |  | ||||||
| // Current export format version. Increase with each format change. |  | ||||||
| // Note: The latest binary (non-indexed) export format is at version 6. |  | ||||||
| //       This exporter is still at level 4, but it doesn't matter since |  | ||||||
| //       the binary importer can handle older versions just fine. |  | ||||||
| // 6: package height (CL 105038) -- NOT IMPLEMENTED HERE |  | ||||||
| // 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMEMTED HERE |  | ||||||
| // 4: type name objects support type aliases, uses aliasTag |  | ||||||
| // 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) |  | ||||||
| // 2: removed unused bool in ODCL export (compiler only) |  | ||||||
| // 1: header format change (more regular), export package for _ struct fields |  | ||||||
| // 0: Go1.7 encoding |  | ||||||
| const exportVersion = 4 |  | ||||||
| 
 |  | ||||||
| // trackAllTypes enables cycle tracking for all types, not just named |  | ||||||
| // types. The existing compiler invariants assume that unnamed types |  | ||||||
| // that are not completely set up are not used, or else there are spurious |  | ||||||
| // errors. |  | ||||||
| // If disabled, only named types are tracked, possibly leading to slightly |  | ||||||
| // less efficient encoding in rare cases. It also prevents the export of |  | ||||||
| // some corner-case type declarations (but those are not handled correctly |  | ||||||
| // with with the textual export format either). |  | ||||||
| // TODO(gri) enable and remove once issues caused by it are fixed |  | ||||||
| const trackAllTypes = false |  | ||||||
| 
 |  | ||||||
| type exporter struct { |  | ||||||
| 	fset *token.FileSet |  | ||||||
| 	out  bytes.Buffer |  | ||||||
| 
 |  | ||||||
| 	// object -> index maps, indexed in order of serialization |  | ||||||
| 	strIndex map[string]int |  | ||||||
| 	pkgIndex map[*types.Package]int |  | ||||||
| 	typIndex map[types.Type]int |  | ||||||
| 
 |  | ||||||
| 	// position encoding |  | ||||||
| 	posInfoFormat bool |  | ||||||
| 	prevFile      string |  | ||||||
| 	prevLine      int |  | ||||||
| 
 |  | ||||||
| 	// debugging support |  | ||||||
| 	written int // bytes written |  | ||||||
| 	indent  int // for trace |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // internalError represents an error generated inside this package. |  | ||||||
| type internalError string |  | ||||||
| 
 |  | ||||||
| func (e internalError) Error() string { return "gcimporter: " + string(e) } |  | ||||||
| 
 |  | ||||||
| func internalErrorf(format string, args ...interface{}) error { |  | ||||||
| 	return internalError(fmt.Sprintf(format, args...)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // BExportData returns binary export data for pkg. |  | ||||||
| // If no file set is provided, position info will be missing. |  | ||||||
| func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { |  | ||||||
| 	defer func() { |  | ||||||
| 		if e := recover(); e != nil { |  | ||||||
| 			if ierr, ok := e.(internalError); ok { |  | ||||||
| 				err = ierr |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			// Not an internal error; panic again. |  | ||||||
| 			panic(e) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	p := exporter{ |  | ||||||
| 		fset:          fset, |  | ||||||
| 		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0 |  | ||||||
| 		pkgIndex:      make(map[*types.Package]int), |  | ||||||
| 		typIndex:      make(map[types.Type]int), |  | ||||||
| 		posInfoFormat: true, // TODO(gri) might become a flag, eventually |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// write version info |  | ||||||
| 	// The version string must start with "version %d" where %d is the version |  | ||||||
| 	// number. Additional debugging information may follow after a blank; that |  | ||||||
| 	// text is ignored by the importer. |  | ||||||
| 	p.rawStringln(fmt.Sprintf("version %d", exportVersion)) |  | ||||||
| 	var debug string |  | ||||||
| 	if debugFormat { |  | ||||||
| 		debug = "debug" |  | ||||||
| 	} |  | ||||||
| 	p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly |  | ||||||
| 	p.bool(trackAllTypes) |  | ||||||
| 	p.bool(p.posInfoFormat) |  | ||||||
| 
 |  | ||||||
| 	// --- generic export data --- |  | ||||||
| 
 |  | ||||||
| 	// populate type map with predeclared "known" types |  | ||||||
| 	for index, typ := range predeclared() { |  | ||||||
| 		p.typIndex[typ] = index |  | ||||||
| 	} |  | ||||||
| 	if len(p.typIndex) != len(predeclared()) { |  | ||||||
| 		return nil, internalError("duplicate entries in type map?") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// write package data |  | ||||||
| 	p.pkg(pkg, true) |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("\n") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// write objects |  | ||||||
| 	objcount := 0 |  | ||||||
| 	scope := pkg.Scope() |  | ||||||
| 	for _, name := range scope.Names() { |  | ||||||
| 		if !ast.IsExported(name) { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if trace { |  | ||||||
| 			p.tracef("\n") |  | ||||||
| 		} |  | ||||||
| 		p.obj(scope.Lookup(name)) |  | ||||||
| 		objcount++ |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// indicate end of list |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("\n") |  | ||||||
| 	} |  | ||||||
| 	p.tag(endTag) |  | ||||||
| 
 |  | ||||||
| 	// for self-verification only (redundant) |  | ||||||
| 	p.int(objcount) |  | ||||||
| 
 |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("\n") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// --- end of export data --- |  | ||||||
| 
 |  | ||||||
| 	return p.out.Bytes(), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) pkg(pkg *types.Package, emptypath bool) { |  | ||||||
| 	if pkg == nil { |  | ||||||
| 		panic(internalError("unexpected nil pkg")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// if we saw the package before, write its index (>= 0) |  | ||||||
| 	if i, ok := p.pkgIndex[pkg]; ok { |  | ||||||
| 		p.index('P', i) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// otherwise, remember the package, write the package tag (< 0) and package data |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("P%d = { ", len(p.pkgIndex)) |  | ||||||
| 		defer p.tracef("} ") |  | ||||||
| 	} |  | ||||||
| 	p.pkgIndex[pkg] = len(p.pkgIndex) |  | ||||||
| 
 |  | ||||||
| 	p.tag(packageTag) |  | ||||||
| 	p.string(pkg.Name()) |  | ||||||
| 	if emptypath { |  | ||||||
| 		p.string("") |  | ||||||
| 	} else { |  | ||||||
| 		p.string(pkg.Path()) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) obj(obj types.Object) { |  | ||||||
| 	switch obj := obj.(type) { |  | ||||||
| 	case *types.Const: |  | ||||||
| 		p.tag(constTag) |  | ||||||
| 		p.pos(obj) |  | ||||||
| 		p.qualifiedName(obj) |  | ||||||
| 		p.typ(obj.Type()) |  | ||||||
| 		p.value(obj.Val()) |  | ||||||
| 
 |  | ||||||
| 	case *types.TypeName: |  | ||||||
| 		if obj.IsAlias() { |  | ||||||
| 			p.tag(aliasTag) |  | ||||||
| 			p.pos(obj) |  | ||||||
| 			p.qualifiedName(obj) |  | ||||||
| 		} else { |  | ||||||
| 			p.tag(typeTag) |  | ||||||
| 		} |  | ||||||
| 		p.typ(obj.Type()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Var: |  | ||||||
| 		p.tag(varTag) |  | ||||||
| 		p.pos(obj) |  | ||||||
| 		p.qualifiedName(obj) |  | ||||||
| 		p.typ(obj.Type()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Func: |  | ||||||
| 		p.tag(funcTag) |  | ||||||
| 		p.pos(obj) |  | ||||||
| 		p.qualifiedName(obj) |  | ||||||
| 		sig := obj.Type().(*types.Signature) |  | ||||||
| 		p.paramList(sig.Params(), sig.Variadic()) |  | ||||||
| 		p.paramList(sig.Results(), false) |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		panic(internalErrorf("unexpected object %v (%T)", obj, obj)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) pos(obj types.Object) { |  | ||||||
| 	if !p.posInfoFormat { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	file, line := p.fileLine(obj) |  | ||||||
| 	if file == p.prevFile { |  | ||||||
| 		// common case: write line delta |  | ||||||
| 		// delta == 0 means different file or no line change |  | ||||||
| 		delta := line - p.prevLine |  | ||||||
| 		p.int(delta) |  | ||||||
| 		if delta == 0 { |  | ||||||
| 			p.int(-1) // -1 means no file change |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		// different file |  | ||||||
| 		p.int(0) |  | ||||||
| 		// Encode filename as length of common prefix with previous |  | ||||||
| 		// filename, followed by (possibly empty) suffix. Filenames |  | ||||||
| 		// frequently share path prefixes, so this can save a lot |  | ||||||
| 		// of space and make export data size less dependent on file |  | ||||||
| 		// path length. The suffix is unlikely to be empty because |  | ||||||
| 		// file names tend to end in ".go". |  | ||||||
| 		n := commonPrefixLen(p.prevFile, file) |  | ||||||
| 		p.int(n)           // n >= 0 |  | ||||||
| 		p.string(file[n:]) // write suffix only |  | ||||||
| 		p.prevFile = file |  | ||||||
| 		p.int(line) |  | ||||||
| 	} |  | ||||||
| 	p.prevLine = line |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) fileLine(obj types.Object) (file string, line int) { |  | ||||||
| 	if p.fset != nil { |  | ||||||
| 		pos := p.fset.Position(obj.Pos()) |  | ||||||
| 		file = pos.Filename |  | ||||||
| 		line = pos.Line |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func commonPrefixLen(a, b string) int { |  | ||||||
| 	if len(a) > len(b) { |  | ||||||
| 		a, b = b, a |  | ||||||
| 	} |  | ||||||
| 	// len(a) <= len(b) |  | ||||||
| 	i := 0 |  | ||||||
| 	for i < len(a) && a[i] == b[i] { |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) qualifiedName(obj types.Object) { |  | ||||||
| 	p.string(obj.Name()) |  | ||||||
| 	p.pkg(obj.Pkg(), false) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) typ(t types.Type) { |  | ||||||
| 	if t == nil { |  | ||||||
| 		panic(internalError("nil type")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Possible optimization: Anonymous pointer types *T where |  | ||||||
| 	// T is a named type are common. We could canonicalize all |  | ||||||
| 	// such types *T to a single type PT = *T. This would lead |  | ||||||
| 	// to at most one *T entry in typIndex, and all future *T's |  | ||||||
| 	// would be encoded as the respective index directly. Would |  | ||||||
| 	// save 1 byte (pointerTag) per *T and reduce the typIndex |  | ||||||
| 	// size (at the cost of a canonicalization map). We can do |  | ||||||
| 	// this later, without encoding format change. |  | ||||||
| 
 |  | ||||||
| 	// if we saw the type before, write its index (>= 0) |  | ||||||
| 	if i, ok := p.typIndex[t]; ok { |  | ||||||
| 		p.index('T', i) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// otherwise, remember the type, write the type tag (< 0) and type data |  | ||||||
| 	if trackAllTypes { |  | ||||||
| 		if trace { |  | ||||||
| 			p.tracef("T%d = {>\n", len(p.typIndex)) |  | ||||||
| 			defer p.tracef("<\n} ") |  | ||||||
| 		} |  | ||||||
| 		p.typIndex[t] = len(p.typIndex) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch t := t.(type) { |  | ||||||
| 	case *types.Named: |  | ||||||
| 		if !trackAllTypes { |  | ||||||
| 			// if we don't track all types, track named types now |  | ||||||
| 			p.typIndex[t] = len(p.typIndex) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		p.tag(namedTag) |  | ||||||
| 		p.pos(t.Obj()) |  | ||||||
| 		p.qualifiedName(t.Obj()) |  | ||||||
| 		p.typ(t.Underlying()) |  | ||||||
| 		if !types.IsInterface(t) { |  | ||||||
| 			p.assocMethods(t) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	case *types.Array: |  | ||||||
| 		p.tag(arrayTag) |  | ||||||
| 		p.int64(t.Len()) |  | ||||||
| 		p.typ(t.Elem()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Slice: |  | ||||||
| 		p.tag(sliceTag) |  | ||||||
| 		p.typ(t.Elem()) |  | ||||||
| 
 |  | ||||||
| 	case *dddSlice: |  | ||||||
| 		p.tag(dddTag) |  | ||||||
| 		p.typ(t.elem) |  | ||||||
| 
 |  | ||||||
| 	case *types.Struct: |  | ||||||
| 		p.tag(structTag) |  | ||||||
| 		p.fieldList(t) |  | ||||||
| 
 |  | ||||||
| 	case *types.Pointer: |  | ||||||
| 		p.tag(pointerTag) |  | ||||||
| 		p.typ(t.Elem()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Signature: |  | ||||||
| 		p.tag(signatureTag) |  | ||||||
| 		p.paramList(t.Params(), t.Variadic()) |  | ||||||
| 		p.paramList(t.Results(), false) |  | ||||||
| 
 |  | ||||||
| 	case *types.Interface: |  | ||||||
| 		p.tag(interfaceTag) |  | ||||||
| 		p.iface(t) |  | ||||||
| 
 |  | ||||||
| 	case *types.Map: |  | ||||||
| 		p.tag(mapTag) |  | ||||||
| 		p.typ(t.Key()) |  | ||||||
| 		p.typ(t.Elem()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Chan: |  | ||||||
| 		p.tag(chanTag) |  | ||||||
| 		p.int(int(3 - t.Dir())) // hack |  | ||||||
| 		p.typ(t.Elem()) |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		panic(internalErrorf("unexpected type %T: %s", t, t)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) assocMethods(named *types.Named) { |  | ||||||
| 	// Sort methods (for determinism). |  | ||||||
| 	var methods []*types.Func |  | ||||||
| 	for i := 0; i < named.NumMethods(); i++ { |  | ||||||
| 		methods = append(methods, named.Method(i)) |  | ||||||
| 	} |  | ||||||
| 	sort.Sort(methodsByName(methods)) |  | ||||||
| 
 |  | ||||||
| 	p.int(len(methods)) |  | ||||||
| 
 |  | ||||||
| 	if trace && methods != nil { |  | ||||||
| 		p.tracef("associated methods {>\n") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i, m := range methods { |  | ||||||
| 		if trace && i > 0 { |  | ||||||
| 			p.tracef("\n") |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		p.pos(m) |  | ||||||
| 		name := m.Name() |  | ||||||
| 		p.string(name) |  | ||||||
| 		if !exported(name) { |  | ||||||
| 			p.pkg(m.Pkg(), false) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		sig := m.Type().(*types.Signature) |  | ||||||
| 		p.paramList(types.NewTuple(sig.Recv()), false) |  | ||||||
| 		p.paramList(sig.Params(), sig.Variadic()) |  | ||||||
| 		p.paramList(sig.Results(), false) |  | ||||||
| 		p.int(0) // dummy value for go:nointerface pragma - ignored by importer |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if trace && methods != nil { |  | ||||||
| 		p.tracef("<\n} ") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type methodsByName []*types.Func |  | ||||||
| 
 |  | ||||||
| func (x methodsByName) Len() int           { return len(x) } |  | ||||||
| func (x methodsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] } |  | ||||||
| func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) fieldList(t *types.Struct) { |  | ||||||
| 	if trace && t.NumFields() > 0 { |  | ||||||
| 		p.tracef("fields {>\n") |  | ||||||
| 		defer p.tracef("<\n} ") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.int(t.NumFields()) |  | ||||||
| 	for i := 0; i < t.NumFields(); i++ { |  | ||||||
| 		if trace && i > 0 { |  | ||||||
| 			p.tracef("\n") |  | ||||||
| 		} |  | ||||||
| 		p.field(t.Field(i)) |  | ||||||
| 		p.string(t.Tag(i)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) field(f *types.Var) { |  | ||||||
| 	if !f.IsField() { |  | ||||||
| 		panic(internalError("field expected")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.pos(f) |  | ||||||
| 	p.fieldName(f) |  | ||||||
| 	p.typ(f.Type()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) iface(t *types.Interface) { |  | ||||||
| 	// TODO(gri): enable importer to load embedded interfaces, |  | ||||||
| 	// then emit Embeddeds and ExplicitMethods separately here. |  | ||||||
| 	p.int(0) |  | ||||||
| 
 |  | ||||||
| 	n := t.NumMethods() |  | ||||||
| 	if trace && n > 0 { |  | ||||||
| 		p.tracef("methods {>\n") |  | ||||||
| 		defer p.tracef("<\n} ") |  | ||||||
| 	} |  | ||||||
| 	p.int(n) |  | ||||||
| 	for i := 0; i < n; i++ { |  | ||||||
| 		if trace && i > 0 { |  | ||||||
| 			p.tracef("\n") |  | ||||||
| 		} |  | ||||||
| 		p.method(t.Method(i)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) method(m *types.Func) { |  | ||||||
| 	sig := m.Type().(*types.Signature) |  | ||||||
| 	if sig.Recv() == nil { |  | ||||||
| 		panic(internalError("method expected")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.pos(m) |  | ||||||
| 	p.string(m.Name()) |  | ||||||
| 	if m.Name() != "_" && !ast.IsExported(m.Name()) { |  | ||||||
| 		p.pkg(m.Pkg(), false) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// interface method; no need to encode receiver. |  | ||||||
| 	p.paramList(sig.Params(), sig.Variadic()) |  | ||||||
| 	p.paramList(sig.Results(), false) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) fieldName(f *types.Var) { |  | ||||||
| 	name := f.Name() |  | ||||||
| 
 |  | ||||||
| 	if f.Anonymous() { |  | ||||||
| 		// anonymous field - we distinguish between 3 cases: |  | ||||||
| 		// 1) field name matches base type name and is exported |  | ||||||
| 		// 2) field name matches base type name and is not exported |  | ||||||
| 		// 3) field name doesn't match base type name (alias name) |  | ||||||
| 		bname := basetypeName(f.Type()) |  | ||||||
| 		if name == bname { |  | ||||||
| 			if ast.IsExported(name) { |  | ||||||
| 				name = "" // 1) we don't need to know the field name or package |  | ||||||
| 			} else { |  | ||||||
| 				name = "?" // 2) use unexported name "?" to force package export |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			// 3) indicate alias and export name as is |  | ||||||
| 			// (this requires an extra "@" but this is a rare case) |  | ||||||
| 			p.string("@") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.string(name) |  | ||||||
| 	if name != "" && !ast.IsExported(name) { |  | ||||||
| 		p.pkg(f.Pkg(), false) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func basetypeName(typ types.Type) string { |  | ||||||
| 	switch typ := deref(typ).(type) { |  | ||||||
| 	case *types.Basic: |  | ||||||
| 		return typ.Name() |  | ||||||
| 	case *types.Named: |  | ||||||
| 		return typ.Obj().Name() |  | ||||||
| 	default: |  | ||||||
| 		return "" // unnamed type |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) paramList(params *types.Tuple, variadic bool) { |  | ||||||
| 	// use negative length to indicate unnamed parameters |  | ||||||
| 	// (look at the first parameter only since either all |  | ||||||
| 	// names are present or all are absent) |  | ||||||
| 	n := params.Len() |  | ||||||
| 	if n > 0 && params.At(0).Name() == "" { |  | ||||||
| 		n = -n |  | ||||||
| 	} |  | ||||||
| 	p.int(n) |  | ||||||
| 	for i := 0; i < params.Len(); i++ { |  | ||||||
| 		q := params.At(i) |  | ||||||
| 		t := q.Type() |  | ||||||
| 		if variadic && i == params.Len()-1 { |  | ||||||
| 			t = &dddSlice{t.(*types.Slice).Elem()} |  | ||||||
| 		} |  | ||||||
| 		p.typ(t) |  | ||||||
| 		if n > 0 { |  | ||||||
| 			name := q.Name() |  | ||||||
| 			p.string(name) |  | ||||||
| 			if name != "_" { |  | ||||||
| 				p.pkg(q.Pkg(), false) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		p.string("") // no compiler-specific info |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) value(x constant.Value) { |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("= ") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch x.Kind() { |  | ||||||
| 	case constant.Bool: |  | ||||||
| 		tag := falseTag |  | ||||||
| 		if constant.BoolVal(x) { |  | ||||||
| 			tag = trueTag |  | ||||||
| 		} |  | ||||||
| 		p.tag(tag) |  | ||||||
| 
 |  | ||||||
| 	case constant.Int: |  | ||||||
| 		if v, exact := constant.Int64Val(x); exact { |  | ||||||
| 			// common case: x fits into an int64 - use compact encoding |  | ||||||
| 			p.tag(int64Tag) |  | ||||||
| 			p.int64(v) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		// uncommon case: large x - use float encoding |  | ||||||
| 		// (powers of 2 will be encoded efficiently with exponent) |  | ||||||
| 		p.tag(floatTag) |  | ||||||
| 		p.float(constant.ToFloat(x)) |  | ||||||
| 
 |  | ||||||
| 	case constant.Float: |  | ||||||
| 		p.tag(floatTag) |  | ||||||
| 		p.float(x) |  | ||||||
| 
 |  | ||||||
| 	case constant.Complex: |  | ||||||
| 		p.tag(complexTag) |  | ||||||
| 		p.float(constant.Real(x)) |  | ||||||
| 		p.float(constant.Imag(x)) |  | ||||||
| 
 |  | ||||||
| 	case constant.String: |  | ||||||
| 		p.tag(stringTag) |  | ||||||
| 		p.string(constant.StringVal(x)) |  | ||||||
| 
 |  | ||||||
| 	case constant.Unknown: |  | ||||||
| 		// package contains type errors |  | ||||||
| 		p.tag(unknownTag) |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		panic(internalErrorf("unexpected value %v (%T)", x, x)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) float(x constant.Value) { |  | ||||||
| 	if x.Kind() != constant.Float { |  | ||||||
| 		panic(internalErrorf("unexpected constant %v, want float", x)) |  | ||||||
| 	} |  | ||||||
| 	// extract sign (there is no -0) |  | ||||||
| 	sign := constant.Sign(x) |  | ||||||
| 	if sign == 0 { |  | ||||||
| 		// x == 0 |  | ||||||
| 		p.int(0) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// x != 0 |  | ||||||
| 
 |  | ||||||
| 	var f big.Float |  | ||||||
| 	if v, exact := constant.Float64Val(x); exact { |  | ||||||
| 		// float64 |  | ||||||
| 		f.SetFloat64(v) |  | ||||||
| 	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { |  | ||||||
| 		// TODO(gri): add big.Rat accessor to constant.Value. |  | ||||||
| 		r := valueToRat(num) |  | ||||||
| 		f.SetRat(r.Quo(r, valueToRat(denom))) |  | ||||||
| 	} else { |  | ||||||
| 		// Value too large to represent as a fraction => inaccessible. |  | ||||||
| 		// TODO(gri): add big.Float accessor to constant.Value. |  | ||||||
| 		f.SetFloat64(math.MaxFloat64) // FIXME |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// extract exponent such that 0.5 <= m < 1.0 |  | ||||||
| 	var m big.Float |  | ||||||
| 	exp := f.MantExp(&m) |  | ||||||
| 
 |  | ||||||
| 	// extract mantissa as *big.Int |  | ||||||
| 	// - set exponent large enough so mant satisfies mant.IsInt() |  | ||||||
| 	// - get *big.Int from mant |  | ||||||
| 	m.SetMantExp(&m, int(m.MinPrec())) |  | ||||||
| 	mant, acc := m.Int(nil) |  | ||||||
| 	if acc != big.Exact { |  | ||||||
| 		panic(internalError("internal error")) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.int(sign) |  | ||||||
| 	p.int(exp) |  | ||||||
| 	p.string(string(mant.Bytes())) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func valueToRat(x constant.Value) *big.Rat { |  | ||||||
| 	// Convert little-endian to big-endian. |  | ||||||
| 	// I can't believe this is necessary. |  | ||||||
| 	bytes := constant.Bytes(x) |  | ||||||
| 	for i := 0; i < len(bytes)/2; i++ { |  | ||||||
| 		bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] |  | ||||||
| 	} |  | ||||||
| 	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) bool(b bool) bool { |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("[") |  | ||||||
| 		defer p.tracef("= %v] ", b) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	x := 0 |  | ||||||
| 	if b { |  | ||||||
| 		x = 1 |  | ||||||
| 	} |  | ||||||
| 	p.int(x) |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ---------------------------------------------------------------------------- |  | ||||||
| // Low-level encoders |  | ||||||
| 
 |  | ||||||
| func (p *exporter) index(marker byte, index int) { |  | ||||||
| 	if index < 0 { |  | ||||||
| 		panic(internalError("invalid index < 0")) |  | ||||||
| 	} |  | ||||||
| 	if debugFormat { |  | ||||||
| 		p.marker('t') |  | ||||||
| 	} |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("%c%d ", marker, index) |  | ||||||
| 	} |  | ||||||
| 	p.rawInt64(int64(index)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) tag(tag int) { |  | ||||||
| 	if tag >= 0 { |  | ||||||
| 		panic(internalError("invalid tag >= 0")) |  | ||||||
| 	} |  | ||||||
| 	if debugFormat { |  | ||||||
| 		p.marker('t') |  | ||||||
| 	} |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("%s ", tagString[-tag]) |  | ||||||
| 	} |  | ||||||
| 	p.rawInt64(int64(tag)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) int(x int) { |  | ||||||
| 	p.int64(int64(x)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) int64(x int64) { |  | ||||||
| 	if debugFormat { |  | ||||||
| 		p.marker('i') |  | ||||||
| 	} |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("%d ", x) |  | ||||||
| 	} |  | ||||||
| 	p.rawInt64(x) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *exporter) string(s string) { |  | ||||||
| 	if debugFormat { |  | ||||||
| 		p.marker('s') |  | ||||||
| 	} |  | ||||||
| 	if trace { |  | ||||||
| 		p.tracef("%q ", s) |  | ||||||
| 	} |  | ||||||
| 	// if we saw the string before, write its index (>= 0) |  | ||||||
| 	// (the empty string is mapped to 0) |  | ||||||
| 	if i, ok := p.strIndex[s]; ok { |  | ||||||
| 		p.rawInt64(int64(i)) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// otherwise, remember string and write its negative length and bytes |  | ||||||
| 	p.strIndex[s] = len(p.strIndex) |  | ||||||
| 	p.rawInt64(-int64(len(s))) |  | ||||||
| 	for i := 0; i < len(s); i++ { |  | ||||||
| 		p.rawByte(s[i]) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // marker emits a marker byte and position information which makes |  | ||||||
| // it easy for a reader to detect if it is "out of sync". Used for |  | ||||||
| // debugFormat format only. |  | ||||||
| func (p *exporter) marker(m byte) { |  | ||||||
| 	p.rawByte(m) |  | ||||||
| 	// Enable this for help tracking down the location |  | ||||||
| 	// of an incorrect marker when running in debugFormat. |  | ||||||
| 	if false && trace { |  | ||||||
| 		p.tracef("#%d ", p.written) |  | ||||||
| 	} |  | ||||||
| 	p.rawInt64(int64(p.written)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // rawInt64 should only be used by low-level encoders. |  | ||||||
| func (p *exporter) rawInt64(x int64) { |  | ||||||
| 	var tmp [binary.MaxVarintLen64]byte |  | ||||||
| 	n := binary.PutVarint(tmp[:], x) |  | ||||||
| 	for i := 0; i < n; i++ { |  | ||||||
| 		p.rawByte(tmp[i]) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // rawStringln should only be used to emit the initial version string. |  | ||||||
| func (p *exporter) rawStringln(s string) { |  | ||||||
| 	for i := 0; i < len(s); i++ { |  | ||||||
| 		p.rawByte(s[i]) |  | ||||||
| 	} |  | ||||||
| 	p.rawByte('\n') |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // rawByte is the bottleneck interface to write to p.out. |  | ||||||
| // rawByte escapes b as follows (any encoding does that |  | ||||||
| // hides '$'): |  | ||||||
| // |  | ||||||
| //	'$'  => '|' 'S' |  | ||||||
| //	'|'  => '|' '|' |  | ||||||
| // |  | ||||||
| // Necessary so other tools can find the end of the |  | ||||||
| // export data by searching for "$$". |  | ||||||
| // rawByte should only be used by low-level encoders. |  | ||||||
| func (p *exporter) rawByte(b byte) { |  | ||||||
| 	switch b { |  | ||||||
| 	case '$': |  | ||||||
| 		// write '$' as '|' 'S' |  | ||||||
| 		b = 'S' |  | ||||||
| 		fallthrough |  | ||||||
| 	case '|': |  | ||||||
| 		// write '|' as '|' '|' |  | ||||||
| 		p.out.WriteByte('|') |  | ||||||
| 		p.written++ |  | ||||||
| 	} |  | ||||||
| 	p.out.WriteByte(b) |  | ||||||
| 	p.written++ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // tracef is like fmt.Printf but it rewrites the format string |  | ||||||
| // to take care of indentation. |  | ||||||
| func (p *exporter) tracef(format string, args ...interface{}) { |  | ||||||
| 	if strings.ContainsAny(format, "<>\n") { |  | ||||||
| 		var buf bytes.Buffer |  | ||||||
| 		for i := 0; i < len(format); i++ { |  | ||||||
| 			// no need to deal with runes |  | ||||||
| 			ch := format[i] |  | ||||||
| 			switch ch { |  | ||||||
| 			case '>': |  | ||||||
| 				p.indent++ |  | ||||||
| 				continue |  | ||||||
| 			case '<': |  | ||||||
| 				p.indent-- |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			buf.WriteByte(ch) |  | ||||||
| 			if ch == '\n' { |  | ||||||
| 				for j := p.indent; j > 0; j-- { |  | ||||||
| 					buf.WriteString(".  ") |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		format = buf.String() |  | ||||||
| 	} |  | ||||||
| 	fmt.Printf(format, args...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Debugging support. |  | ||||||
| // (tagString is only used when tracing is enabled) |  | ||||||
| var tagString = [...]string{ |  | ||||||
| 	// Packages |  | ||||||
| 	-packageTag: "package", |  | ||||||
| 
 |  | ||||||
| 	// Types |  | ||||||
| 	-namedTag:     "named type", |  | ||||||
| 	-arrayTag:     "array", |  | ||||||
| 	-sliceTag:     "slice", |  | ||||||
| 	-dddTag:       "ddd", |  | ||||||
| 	-structTag:    "struct", |  | ||||||
| 	-pointerTag:   "pointer", |  | ||||||
| 	-signatureTag: "signature", |  | ||||||
| 	-interfaceTag: "interface", |  | ||||||
| 	-mapTag:       "map", |  | ||||||
| 	-chanTag:      "chan", |  | ||||||
| 
 |  | ||||||
| 	// Values |  | ||||||
| 	-falseTag:    "false", |  | ||||||
| 	-trueTag:     "true", |  | ||||||
| 	-int64Tag:    "int64", |  | ||||||
| 	-floatTag:    "float", |  | ||||||
| 	-fractionTag: "fraction", |  | ||||||
| 	-complexTag:  "complex", |  | ||||||
| 	-stringTag:   "string", |  | ||||||
| 	-unknownTag:  "unknown", |  | ||||||
| 
 |  | ||||||
| 	// Type aliases |  | ||||||
| 	-aliasTag: "alias", |  | ||||||
| } |  | ||||||
							
								
								
									
										1039
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1039
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										93
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										93
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,93 +0,0 @@ | |||||||
| // Copyright 2011 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go. |  | ||||||
| 
 |  | ||||||
| // This file implements FindExportData. |  | ||||||
| 
 |  | ||||||
| package gcimporter |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bufio" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { |  | ||||||
| 	// See $GOROOT/include/ar.h. |  | ||||||
| 	hdr := make([]byte, 16+12+6+6+8+10+2) |  | ||||||
| 	_, err = io.ReadFull(r, hdr) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// leave for debugging |  | ||||||
| 	if false { |  | ||||||
| 		fmt.Printf("header: %s", hdr) |  | ||||||
| 	} |  | ||||||
| 	s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) |  | ||||||
| 	size, err = strconv.Atoi(s) |  | ||||||
| 	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { |  | ||||||
| 		err = fmt.Errorf("invalid archive header") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	name = strings.TrimSpace(string(hdr[:16])) |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // FindExportData positions the reader r at the beginning of the |  | ||||||
| // export data section of an underlying GC-created object/archive |  | ||||||
| // file by reading from it. The reader must be positioned at the |  | ||||||
| // start of the file before calling this function. The hdr result |  | ||||||
| // is the string before the export data, either "$$" or "$$B". |  | ||||||
| // |  | ||||||
| func FindExportData(r *bufio.Reader) (hdr string, err error) { |  | ||||||
| 	// Read first line to make sure this is an object file. |  | ||||||
| 	line, err := r.ReadSlice('\n') |  | ||||||
| 	if err != nil { |  | ||||||
| 		err = fmt.Errorf("can't find export data (%v)", err) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if string(line) == "!<arch>\n" { |  | ||||||
| 		// Archive file. Scan to __.PKGDEF. |  | ||||||
| 		var name string |  | ||||||
| 		if name, _, err = readGopackHeader(r); err != nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// First entry should be __.PKGDEF. |  | ||||||
| 		if name != "__.PKGDEF" { |  | ||||||
| 			err = fmt.Errorf("go archive is missing __.PKGDEF") |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Read first line of __.PKGDEF data, so that line |  | ||||||
| 		// is once again the first line of the input. |  | ||||||
| 		if line, err = r.ReadSlice('\n'); err != nil { |  | ||||||
| 			err = fmt.Errorf("can't find export data (%v)", err) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Now at __.PKGDEF in archive or still at beginning of file. |  | ||||||
| 	// Either way, line should begin with "go object ". |  | ||||||
| 	if !strings.HasPrefix(string(line), "go object ") { |  | ||||||
| 		err = fmt.Errorf("not a Go object file") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Skip over object header to export data. |  | ||||||
| 	// Begins after first line starting with $$. |  | ||||||
| 	for line[0] != '$' { |  | ||||||
| 		if line, err = r.ReadSlice('\n'); err != nil { |  | ||||||
| 			err = fmt.Errorf("can't find export data (%v)", err) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	hdr = string(line) |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
							
								
								
									
										1078
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1078
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										739
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										739
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,739 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Indexed binary package export. |  | ||||||
| // This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; |  | ||||||
| // see that file for specification of the format. |  | ||||||
| 
 |  | ||||||
| package gcimporter |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/binary" |  | ||||||
| 	"go/ast" |  | ||||||
| 	"go/constant" |  | ||||||
| 	"go/token" |  | ||||||
| 	"go/types" |  | ||||||
| 	"io" |  | ||||||
| 	"math/big" |  | ||||||
| 	"reflect" |  | ||||||
| 	"sort" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Current indexed export format version. Increase with each format change. |  | ||||||
| // 0: Go1.11 encoding |  | ||||||
| const iexportVersion = 0 |  | ||||||
| 
 |  | ||||||
| // IExportData returns the binary export data for pkg. |  | ||||||
| // |  | ||||||
| // If no file set is provided, position info will be missing. |  | ||||||
| // The package path of the top-level package will not be recorded, |  | ||||||
| // so that calls to IImportData can override with a provided package path. |  | ||||||
| func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { |  | ||||||
| 	defer func() { |  | ||||||
| 		if e := recover(); e != nil { |  | ||||||
| 			if ierr, ok := e.(internalError); ok { |  | ||||||
| 				err = ierr |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			// Not an internal error; panic again. |  | ||||||
| 			panic(e) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	p := iexporter{ |  | ||||||
| 		out:         bytes.NewBuffer(nil), |  | ||||||
| 		fset:        fset, |  | ||||||
| 		allPkgs:     map[*types.Package]bool{}, |  | ||||||
| 		stringIndex: map[string]uint64{}, |  | ||||||
| 		declIndex:   map[types.Object]uint64{}, |  | ||||||
| 		typIndex:    map[types.Type]uint64{}, |  | ||||||
| 		localpkg:    pkg, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i, pt := range predeclared() { |  | ||||||
| 		p.typIndex[pt] = uint64(i) |  | ||||||
| 	} |  | ||||||
| 	if len(p.typIndex) > predeclReserved { |  | ||||||
| 		panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Initialize work queue with exported declarations. |  | ||||||
| 	scope := pkg.Scope() |  | ||||||
| 	for _, name := range scope.Names() { |  | ||||||
| 		if ast.IsExported(name) { |  | ||||||
| 			p.pushDecl(scope.Lookup(name)) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Loop until no more work. |  | ||||||
| 	for !p.declTodo.empty() { |  | ||||||
| 		p.doDecl(p.declTodo.popHead()) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Append indices to data0 section. |  | ||||||
| 	dataLen := uint64(p.data0.Len()) |  | ||||||
| 	w := p.newWriter() |  | ||||||
| 	w.writeIndex(p.declIndex) |  | ||||||
| 	w.flush() |  | ||||||
| 
 |  | ||||||
| 	// Assemble header. |  | ||||||
| 	var hdr intWriter |  | ||||||
| 	hdr.WriteByte('i') |  | ||||||
| 	hdr.uint64(iexportVersion) |  | ||||||
| 	hdr.uint64(uint64(p.strings.Len())) |  | ||||||
| 	hdr.uint64(dataLen) |  | ||||||
| 
 |  | ||||||
| 	// Flush output. |  | ||||||
| 	io.Copy(p.out, &hdr) |  | ||||||
| 	io.Copy(p.out, &p.strings) |  | ||||||
| 	io.Copy(p.out, &p.data0) |  | ||||||
| 
 |  | ||||||
| 	return p.out.Bytes(), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // writeIndex writes out an object index. mainIndex indicates whether |  | ||||||
| // we're writing out the main index, which is also read by |  | ||||||
| // non-compiler tools and includes a complete package description |  | ||||||
| // (i.e., name and height). |  | ||||||
| func (w *exportWriter) writeIndex(index map[types.Object]uint64) { |  | ||||||
| 	// Build a map from packages to objects from that package. |  | ||||||
| 	pkgObjs := map[*types.Package][]types.Object{} |  | ||||||
| 
 |  | ||||||
| 	// For the main index, make sure to include every package that |  | ||||||
| 	// we reference, even if we're not exporting (or reexporting) |  | ||||||
| 	// any symbols from it. |  | ||||||
| 	pkgObjs[w.p.localpkg] = nil |  | ||||||
| 	for pkg := range w.p.allPkgs { |  | ||||||
| 		pkgObjs[pkg] = nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for obj := range index { |  | ||||||
| 		pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], obj) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var pkgs []*types.Package |  | ||||||
| 	for pkg, objs := range pkgObjs { |  | ||||||
| 		pkgs = append(pkgs, pkg) |  | ||||||
| 
 |  | ||||||
| 		sort.Slice(objs, func(i, j int) bool { |  | ||||||
| 			return objs[i].Name() < objs[j].Name() |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	sort.Slice(pkgs, func(i, j int) bool { |  | ||||||
| 		return w.exportPath(pkgs[i]) < w.exportPath(pkgs[j]) |  | ||||||
| 	}) |  | ||||||
| 
 |  | ||||||
| 	w.uint64(uint64(len(pkgs))) |  | ||||||
| 	for _, pkg := range pkgs { |  | ||||||
| 		w.string(w.exportPath(pkg)) |  | ||||||
| 		w.string(pkg.Name()) |  | ||||||
| 		w.uint64(uint64(0)) // package height is not needed for go/types |  | ||||||
| 
 |  | ||||||
| 		objs := pkgObjs[pkg] |  | ||||||
| 		w.uint64(uint64(len(objs))) |  | ||||||
| 		for _, obj := range objs { |  | ||||||
| 			w.string(obj.Name()) |  | ||||||
| 			w.uint64(index[obj]) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type iexporter struct { |  | ||||||
| 	fset *token.FileSet |  | ||||||
| 	out  *bytes.Buffer |  | ||||||
| 
 |  | ||||||
| 	localpkg *types.Package |  | ||||||
| 
 |  | ||||||
| 	// allPkgs tracks all packages that have been referenced by |  | ||||||
| 	// the export data, so we can ensure to include them in the |  | ||||||
| 	// main index. |  | ||||||
| 	allPkgs map[*types.Package]bool |  | ||||||
| 
 |  | ||||||
| 	declTodo objQueue |  | ||||||
| 
 |  | ||||||
| 	strings     intWriter |  | ||||||
| 	stringIndex map[string]uint64 |  | ||||||
| 
 |  | ||||||
| 	data0     intWriter |  | ||||||
| 	declIndex map[types.Object]uint64 |  | ||||||
| 	typIndex  map[types.Type]uint64 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // stringOff returns the offset of s within the string section. |  | ||||||
| // If not already present, it's added to the end. |  | ||||||
| func (p *iexporter) stringOff(s string) uint64 { |  | ||||||
| 	off, ok := p.stringIndex[s] |  | ||||||
| 	if !ok { |  | ||||||
| 		off = uint64(p.strings.Len()) |  | ||||||
| 		p.stringIndex[s] = off |  | ||||||
| 
 |  | ||||||
| 		p.strings.uint64(uint64(len(s))) |  | ||||||
| 		p.strings.WriteString(s) |  | ||||||
| 	} |  | ||||||
| 	return off |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // pushDecl adds n to the declaration work queue, if not already present. |  | ||||||
| func (p *iexporter) pushDecl(obj types.Object) { |  | ||||||
| 	// Package unsafe is known to the compiler and predeclared. |  | ||||||
| 	assert(obj.Pkg() != types.Unsafe) |  | ||||||
| 
 |  | ||||||
| 	if _, ok := p.declIndex[obj]; ok { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.declIndex[obj] = ^uint64(0) // mark n present in work queue |  | ||||||
| 	p.declTodo.pushTail(obj) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // exportWriter handles writing out individual data section chunks. |  | ||||||
| type exportWriter struct { |  | ||||||
| 	p *iexporter |  | ||||||
| 
 |  | ||||||
| 	data     intWriter |  | ||||||
| 	currPkg  *types.Package |  | ||||||
| 	prevFile string |  | ||||||
| 	prevLine int64 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) exportPath(pkg *types.Package) string { |  | ||||||
| 	if pkg == w.p.localpkg { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	return pkg.Path() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iexporter) doDecl(obj types.Object) { |  | ||||||
| 	w := p.newWriter() |  | ||||||
| 	w.setPkg(obj.Pkg(), false) |  | ||||||
| 
 |  | ||||||
| 	switch obj := obj.(type) { |  | ||||||
| 	case *types.Var: |  | ||||||
| 		w.tag('V') |  | ||||||
| 		w.pos(obj.Pos()) |  | ||||||
| 		w.typ(obj.Type(), obj.Pkg()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Func: |  | ||||||
| 		sig, _ := obj.Type().(*types.Signature) |  | ||||||
| 		if sig.Recv() != nil { |  | ||||||
| 			panic(internalErrorf("unexpected method: %v", sig)) |  | ||||||
| 		} |  | ||||||
| 		w.tag('F') |  | ||||||
| 		w.pos(obj.Pos()) |  | ||||||
| 		w.signature(sig) |  | ||||||
| 
 |  | ||||||
| 	case *types.Const: |  | ||||||
| 		w.tag('C') |  | ||||||
| 		w.pos(obj.Pos()) |  | ||||||
| 		w.value(obj.Type(), obj.Val()) |  | ||||||
| 
 |  | ||||||
| 	case *types.TypeName: |  | ||||||
| 		if obj.IsAlias() { |  | ||||||
| 			w.tag('A') |  | ||||||
| 			w.pos(obj.Pos()) |  | ||||||
| 			w.typ(obj.Type(), obj.Pkg()) |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Defined type. |  | ||||||
| 		w.tag('T') |  | ||||||
| 		w.pos(obj.Pos()) |  | ||||||
| 
 |  | ||||||
| 		underlying := obj.Type().Underlying() |  | ||||||
| 		w.typ(underlying, obj.Pkg()) |  | ||||||
| 
 |  | ||||||
| 		t := obj.Type() |  | ||||||
| 		if types.IsInterface(t) { |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		named, ok := t.(*types.Named) |  | ||||||
| 		if !ok { |  | ||||||
| 			panic(internalErrorf("%s is not a defined type", t)) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		n := named.NumMethods() |  | ||||||
| 		w.uint64(uint64(n)) |  | ||||||
| 		for i := 0; i < n; i++ { |  | ||||||
| 			m := named.Method(i) |  | ||||||
| 			w.pos(m.Pos()) |  | ||||||
| 			w.string(m.Name()) |  | ||||||
| 			sig, _ := m.Type().(*types.Signature) |  | ||||||
| 			w.param(sig.Recv()) |  | ||||||
| 			w.signature(sig) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		panic(internalErrorf("unexpected object: %v", obj)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p.declIndex[obj] = w.flush() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) tag(tag byte) { |  | ||||||
| 	w.data.WriteByte(tag) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) pos(pos token.Pos) { |  | ||||||
| 	if w.p.fset == nil { |  | ||||||
| 		w.int64(0) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	p := w.p.fset.Position(pos) |  | ||||||
| 	file := p.Filename |  | ||||||
| 	line := int64(p.Line) |  | ||||||
| 
 |  | ||||||
| 	// When file is the same as the last position (common case), |  | ||||||
| 	// we can save a few bytes by delta encoding just the line |  | ||||||
| 	// number. |  | ||||||
| 	// |  | ||||||
| 	// Note: Because data objects may be read out of order (or not |  | ||||||
| 	// at all), we can only apply delta encoding within a single |  | ||||||
| 	// object. This is handled implicitly by tracking prevFile and |  | ||||||
| 	// prevLine as fields of exportWriter. |  | ||||||
| 
 |  | ||||||
| 	if file == w.prevFile { |  | ||||||
| 		delta := line - w.prevLine |  | ||||||
| 		w.int64(delta) |  | ||||||
| 		if delta == deltaNewFile { |  | ||||||
| 			w.int64(-1) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		w.int64(deltaNewFile) |  | ||||||
| 		w.int64(line) // line >= 0 |  | ||||||
| 		w.string(file) |  | ||||||
| 		w.prevFile = file |  | ||||||
| 	} |  | ||||||
| 	w.prevLine = line |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) pkg(pkg *types.Package) { |  | ||||||
| 	// Ensure any referenced packages are declared in the main index. |  | ||||||
| 	w.p.allPkgs[pkg] = true |  | ||||||
| 
 |  | ||||||
| 	w.string(w.exportPath(pkg)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) qualifiedIdent(obj types.Object) { |  | ||||||
| 	// Ensure any referenced declarations are written out too. |  | ||||||
| 	w.p.pushDecl(obj) |  | ||||||
| 
 |  | ||||||
| 	w.string(obj.Name()) |  | ||||||
| 	w.pkg(obj.Pkg()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) typ(t types.Type, pkg *types.Package) { |  | ||||||
| 	w.data.uint64(w.p.typOff(t, pkg)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iexporter) newWriter() *exportWriter { |  | ||||||
| 	return &exportWriter{p: p} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) flush() uint64 { |  | ||||||
| 	off := uint64(w.p.data0.Len()) |  | ||||||
| 	io.Copy(&w.p.data0, &w.data) |  | ||||||
| 	return off |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { |  | ||||||
| 	off, ok := p.typIndex[t] |  | ||||||
| 	if !ok { |  | ||||||
| 		w := p.newWriter() |  | ||||||
| 		w.doTyp(t, pkg) |  | ||||||
| 		off = predeclReserved + w.flush() |  | ||||||
| 		p.typIndex[t] = off |  | ||||||
| 	} |  | ||||||
| 	return off |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) startType(k itag) { |  | ||||||
| 	w.data.uint64(uint64(k)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { |  | ||||||
| 	switch t := t.(type) { |  | ||||||
| 	case *types.Named: |  | ||||||
| 		w.startType(definedType) |  | ||||||
| 		w.qualifiedIdent(t.Obj()) |  | ||||||
| 
 |  | ||||||
| 	case *types.Pointer: |  | ||||||
| 		w.startType(pointerType) |  | ||||||
| 		w.typ(t.Elem(), pkg) |  | ||||||
| 
 |  | ||||||
| 	case *types.Slice: |  | ||||||
| 		w.startType(sliceType) |  | ||||||
| 		w.typ(t.Elem(), pkg) |  | ||||||
| 
 |  | ||||||
| 	case *types.Array: |  | ||||||
| 		w.startType(arrayType) |  | ||||||
| 		w.uint64(uint64(t.Len())) |  | ||||||
| 		w.typ(t.Elem(), pkg) |  | ||||||
| 
 |  | ||||||
| 	case *types.Chan: |  | ||||||
| 		w.startType(chanType) |  | ||||||
| 		// 1 RecvOnly; 2 SendOnly; 3 SendRecv |  | ||||||
| 		var dir uint64 |  | ||||||
| 		switch t.Dir() { |  | ||||||
| 		case types.RecvOnly: |  | ||||||
| 			dir = 1 |  | ||||||
| 		case types.SendOnly: |  | ||||||
| 			dir = 2 |  | ||||||
| 		case types.SendRecv: |  | ||||||
| 			dir = 3 |  | ||||||
| 		} |  | ||||||
| 		w.uint64(dir) |  | ||||||
| 		w.typ(t.Elem(), pkg) |  | ||||||
| 
 |  | ||||||
| 	case *types.Map: |  | ||||||
| 		w.startType(mapType) |  | ||||||
| 		w.typ(t.Key(), pkg) |  | ||||||
| 		w.typ(t.Elem(), pkg) |  | ||||||
| 
 |  | ||||||
| 	case *types.Signature: |  | ||||||
| 		w.startType(signatureType) |  | ||||||
| 		w.setPkg(pkg, true) |  | ||||||
| 		w.signature(t) |  | ||||||
| 
 |  | ||||||
| 	case *types.Struct: |  | ||||||
| 		w.startType(structType) |  | ||||||
| 		w.setPkg(pkg, true) |  | ||||||
| 
 |  | ||||||
| 		n := t.NumFields() |  | ||||||
| 		w.uint64(uint64(n)) |  | ||||||
| 		for i := 0; i < n; i++ { |  | ||||||
| 			f := t.Field(i) |  | ||||||
| 			w.pos(f.Pos()) |  | ||||||
| 			w.string(f.Name()) |  | ||||||
| 			w.typ(f.Type(), pkg) |  | ||||||
| 			w.bool(f.Anonymous()) |  | ||||||
| 			w.string(t.Tag(i)) // note (or tag) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	case *types.Interface: |  | ||||||
| 		w.startType(interfaceType) |  | ||||||
| 		w.setPkg(pkg, true) |  | ||||||
| 
 |  | ||||||
| 		n := t.NumEmbeddeds() |  | ||||||
| 		w.uint64(uint64(n)) |  | ||||||
| 		for i := 0; i < n; i++ { |  | ||||||
| 			f := t.Embedded(i) |  | ||||||
| 			w.pos(f.Obj().Pos()) |  | ||||||
| 			w.typ(f.Obj().Type(), f.Obj().Pkg()) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		n = t.NumExplicitMethods() |  | ||||||
| 		w.uint64(uint64(n)) |  | ||||||
| 		for i := 0; i < n; i++ { |  | ||||||
| 			m := t.ExplicitMethod(i) |  | ||||||
| 			w.pos(m.Pos()) |  | ||||||
| 			w.string(m.Name()) |  | ||||||
| 			sig, _ := m.Type().(*types.Signature) |  | ||||||
| 			w.signature(sig) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t))) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) setPkg(pkg *types.Package, write bool) { |  | ||||||
| 	if write { |  | ||||||
| 		w.pkg(pkg) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	w.currPkg = pkg |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) signature(sig *types.Signature) { |  | ||||||
| 	w.paramList(sig.Params()) |  | ||||||
| 	w.paramList(sig.Results()) |  | ||||||
| 	if sig.Params().Len() > 0 { |  | ||||||
| 		w.bool(sig.Variadic()) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) paramList(tup *types.Tuple) { |  | ||||||
| 	n := tup.Len() |  | ||||||
| 	w.uint64(uint64(n)) |  | ||||||
| 	for i := 0; i < n; i++ { |  | ||||||
| 		w.param(tup.At(i)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) param(obj types.Object) { |  | ||||||
| 	w.pos(obj.Pos()) |  | ||||||
| 	w.localIdent(obj) |  | ||||||
| 	w.typ(obj.Type(), obj.Pkg()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) value(typ types.Type, v constant.Value) { |  | ||||||
| 	w.typ(typ, nil) |  | ||||||
| 
 |  | ||||||
| 	switch v.Kind() { |  | ||||||
| 	case constant.Bool: |  | ||||||
| 		w.bool(constant.BoolVal(v)) |  | ||||||
| 	case constant.Int: |  | ||||||
| 		var i big.Int |  | ||||||
| 		if i64, exact := constant.Int64Val(v); exact { |  | ||||||
| 			i.SetInt64(i64) |  | ||||||
| 		} else if ui64, exact := constant.Uint64Val(v); exact { |  | ||||||
| 			i.SetUint64(ui64) |  | ||||||
| 		} else { |  | ||||||
| 			i.SetString(v.ExactString(), 10) |  | ||||||
| 		} |  | ||||||
| 		w.mpint(&i, typ) |  | ||||||
| 	case constant.Float: |  | ||||||
| 		f := constantToFloat(v) |  | ||||||
| 		w.mpfloat(f, typ) |  | ||||||
| 	case constant.Complex: |  | ||||||
| 		w.mpfloat(constantToFloat(constant.Real(v)), typ) |  | ||||||
| 		w.mpfloat(constantToFloat(constant.Imag(v)), typ) |  | ||||||
| 	case constant.String: |  | ||||||
| 		w.string(constant.StringVal(v)) |  | ||||||
| 	case constant.Unknown: |  | ||||||
| 		// package contains type errors |  | ||||||
| 	default: |  | ||||||
| 		panic(internalErrorf("unexpected value %v (%T)", v, v)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // constantToFloat converts a constant.Value with kind constant.Float to a |  | ||||||
| // big.Float. |  | ||||||
| func constantToFloat(x constant.Value) *big.Float { |  | ||||||
| 	assert(x.Kind() == constant.Float) |  | ||||||
| 	// Use the same floating-point precision (512) as cmd/compile |  | ||||||
| 	// (see Mpprec in cmd/compile/internal/gc/mpfloat.go). |  | ||||||
| 	const mpprec = 512 |  | ||||||
| 	var f big.Float |  | ||||||
| 	f.SetPrec(mpprec) |  | ||||||
| 	if v, exact := constant.Float64Val(x); exact { |  | ||||||
| 		// float64 |  | ||||||
| 		f.SetFloat64(v) |  | ||||||
| 	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { |  | ||||||
| 		// TODO(gri): add big.Rat accessor to constant.Value. |  | ||||||
| 		n := valueToRat(num) |  | ||||||
| 		d := valueToRat(denom) |  | ||||||
| 		f.SetRat(n.Quo(n, d)) |  | ||||||
| 	} else { |  | ||||||
| 		// Value too large to represent as a fraction => inaccessible. |  | ||||||
| 		// TODO(gri): add big.Float accessor to constant.Value. |  | ||||||
| 		_, ok := f.SetString(x.ExactString()) |  | ||||||
| 		assert(ok) |  | ||||||
| 	} |  | ||||||
| 	return &f |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // mpint exports a multi-precision integer. |  | ||||||
| // |  | ||||||
| // For unsigned types, small values are written out as a single |  | ||||||
| // byte. Larger values are written out as a length-prefixed big-endian |  | ||||||
| // byte string, where the length prefix is encoded as its complement. |  | ||||||
| // For example, bytes 0, 1, and 2 directly represent the integer |  | ||||||
| // values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, |  | ||||||
| // 2-, and 3-byte big-endian string follow. |  | ||||||
| // |  | ||||||
| // Encoding for signed types use the same general approach as for |  | ||||||
| // unsigned types, except small values use zig-zag encoding and the |  | ||||||
| // bottom bit of length prefix byte for large values is reserved as a |  | ||||||
| // sign bit. |  | ||||||
| // |  | ||||||
| // The exact boundary between small and large encodings varies |  | ||||||
| // according to the maximum number of bytes needed to encode a value |  | ||||||
| // of type typ. As a special case, 8-bit types are always encoded as a |  | ||||||
| // single byte. |  | ||||||
| // |  | ||||||
| // TODO(mdempsky): Is this level of complexity really worthwhile? |  | ||||||
| func (w *exportWriter) mpint(x *big.Int, typ types.Type) { |  | ||||||
| 	basic, ok := typ.Underlying().(*types.Basic) |  | ||||||
| 	if !ok { |  | ||||||
| 		panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying())) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	signed, maxBytes := intSize(basic) |  | ||||||
| 
 |  | ||||||
| 	negative := x.Sign() < 0 |  | ||||||
| 	if !signed && negative { |  | ||||||
| 		panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	b := x.Bytes() |  | ||||||
| 	if len(b) > 0 && b[0] == 0 { |  | ||||||
| 		panic(internalErrorf("leading zeros")) |  | ||||||
| 	} |  | ||||||
| 	if uint(len(b)) > maxBytes { |  | ||||||
| 		panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	maxSmall := 256 - maxBytes |  | ||||||
| 	if signed { |  | ||||||
| 		maxSmall = 256 - 2*maxBytes |  | ||||||
| 	} |  | ||||||
| 	if maxBytes == 1 { |  | ||||||
| 		maxSmall = 256 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Check if x can use small value encoding. |  | ||||||
| 	if len(b) <= 1 { |  | ||||||
| 		var ux uint |  | ||||||
| 		if len(b) == 1 { |  | ||||||
| 			ux = uint(b[0]) |  | ||||||
| 		} |  | ||||||
| 		if signed { |  | ||||||
| 			ux <<= 1 |  | ||||||
| 			if negative { |  | ||||||
| 				ux-- |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if ux < maxSmall { |  | ||||||
| 			w.data.WriteByte(byte(ux)) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	n := 256 - uint(len(b)) |  | ||||||
| 	if signed { |  | ||||||
| 		n = 256 - 2*uint(len(b)) |  | ||||||
| 		if negative { |  | ||||||
| 			n |= 1 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if n < maxSmall || n >= 256 { |  | ||||||
| 		panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	w.data.WriteByte(byte(n)) |  | ||||||
| 	w.data.Write(b) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // mpfloat exports a multi-precision floating point number. |  | ||||||
| // |  | ||||||
| // The number's value is decomposed into mantissa × 2**exponent, where |  | ||||||
| // mantissa is an integer. The value is written out as mantissa (as a |  | ||||||
| // multi-precision integer) and then the exponent, except exponent is |  | ||||||
| // omitted if mantissa is zero. |  | ||||||
| func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { |  | ||||||
| 	if f.IsInf() { |  | ||||||
| 		panic("infinite constant") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Break into f = mant × 2**exp, with 0.5 <= mant < 1. |  | ||||||
| 	var mant big.Float |  | ||||||
| 	exp := int64(f.MantExp(&mant)) |  | ||||||
| 
 |  | ||||||
| 	// Scale so that mant is an integer. |  | ||||||
| 	prec := mant.MinPrec() |  | ||||||
| 	mant.SetMantExp(&mant, int(prec)) |  | ||||||
| 	exp -= int64(prec) |  | ||||||
| 
 |  | ||||||
| 	manti, acc := mant.Int(nil) |  | ||||||
| 	if acc != big.Exact { |  | ||||||
| 		panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc)) |  | ||||||
| 	} |  | ||||||
| 	w.mpint(manti, typ) |  | ||||||
| 	if manti.Sign() != 0 { |  | ||||||
| 		w.int64(exp) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) bool(b bool) bool { |  | ||||||
| 	var x uint64 |  | ||||||
| 	if b { |  | ||||||
| 		x = 1 |  | ||||||
| 	} |  | ||||||
| 	w.uint64(x) |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) int64(x int64)   { w.data.int64(x) } |  | ||||||
| func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) } |  | ||||||
| func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) } |  | ||||||
| 
 |  | ||||||
| func (w *exportWriter) localIdent(obj types.Object) { |  | ||||||
| 	// Anonymous parameters. |  | ||||||
| 	if obj == nil { |  | ||||||
| 		w.string("") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	name := obj.Name() |  | ||||||
| 	if name == "_" { |  | ||||||
| 		w.string("_") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	w.string(name) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type intWriter struct { |  | ||||||
| 	bytes.Buffer |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *intWriter) int64(x int64) { |  | ||||||
| 	var buf [binary.MaxVarintLen64]byte |  | ||||||
| 	n := binary.PutVarint(buf[:], x) |  | ||||||
| 	w.Write(buf[:n]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (w *intWriter) uint64(x uint64) { |  | ||||||
| 	var buf [binary.MaxVarintLen64]byte |  | ||||||
| 	n := binary.PutUvarint(buf[:], x) |  | ||||||
| 	w.Write(buf[:n]) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func assert(cond bool) { |  | ||||||
| 	if !cond { |  | ||||||
| 		panic("internal error: assertion failed") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // The below is copied from go/src/cmd/compile/internal/gc/syntax.go. |  | ||||||
| 
 |  | ||||||
| // objQueue is a FIFO queue of types.Object. The zero value of objQueue is |  | ||||||
| // a ready-to-use empty queue. |  | ||||||
| type objQueue struct { |  | ||||||
| 	ring       []types.Object |  | ||||||
| 	head, tail int |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // empty returns true if q contains no Nodes. |  | ||||||
| func (q *objQueue) empty() bool { |  | ||||||
| 	return q.head == q.tail |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // pushTail appends n to the tail of the queue. |  | ||||||
| func (q *objQueue) pushTail(obj types.Object) { |  | ||||||
| 	if len(q.ring) == 0 { |  | ||||||
| 		q.ring = make([]types.Object, 16) |  | ||||||
| 	} else if q.head+len(q.ring) == q.tail { |  | ||||||
| 		// Grow the ring. |  | ||||||
| 		nring := make([]types.Object, len(q.ring)*2) |  | ||||||
| 		// Copy the old elements. |  | ||||||
| 		part := q.ring[q.head%len(q.ring):] |  | ||||||
| 		if q.tail-q.head <= len(part) { |  | ||||||
| 			part = part[:q.tail-q.head] |  | ||||||
| 			copy(nring, part) |  | ||||||
| 		} else { |  | ||||||
| 			pos := copy(nring, part) |  | ||||||
| 			copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) |  | ||||||
| 		} |  | ||||||
| 		q.ring, q.head, q.tail = nring, 0, q.tail-q.head |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	q.ring[q.tail%len(q.ring)] = obj |  | ||||||
| 	q.tail++ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // popHead pops a node from the head of the queue. It panics if q is empty. |  | ||||||
| func (q *objQueue) popHead() types.Object { |  | ||||||
| 	if q.empty() { |  | ||||||
| 		panic("dequeue empty") |  | ||||||
| 	} |  | ||||||
| 	obj := q.ring[q.head%len(q.ring)] |  | ||||||
| 	q.head++ |  | ||||||
| 	return obj |  | ||||||
| } |  | ||||||
							
								
								
									
										630
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										630
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,630 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Indexed package import. |  | ||||||
| // See cmd/compile/internal/gc/iexport.go for the export data format. |  | ||||||
| 
 |  | ||||||
| // This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go. |  | ||||||
| 
 |  | ||||||
| package gcimporter |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/binary" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/constant" |  | ||||||
| 	"go/token" |  | ||||||
| 	"go/types" |  | ||||||
| 	"io" |  | ||||||
| 	"sort" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type intReader struct { |  | ||||||
| 	*bytes.Reader |  | ||||||
| 	path string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *intReader) int64() int64 { |  | ||||||
| 	i, err := binary.ReadVarint(r.Reader) |  | ||||||
| 	if err != nil { |  | ||||||
| 		errorf("import %q: read varint error: %v", r.path, err) |  | ||||||
| 	} |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *intReader) uint64() uint64 { |  | ||||||
| 	i, err := binary.ReadUvarint(r.Reader) |  | ||||||
| 	if err != nil { |  | ||||||
| 		errorf("import %q: read varint error: %v", r.path, err) |  | ||||||
| 	} |  | ||||||
| 	return i |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const predeclReserved = 32 |  | ||||||
| 
 |  | ||||||
| type itag uint64 |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	// Types |  | ||||||
| 	definedType itag = iota |  | ||||||
| 	pointerType |  | ||||||
| 	sliceType |  | ||||||
| 	arrayType |  | ||||||
| 	chanType |  | ||||||
| 	mapType |  | ||||||
| 	signatureType |  | ||||||
| 	structType |  | ||||||
| 	interfaceType |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // IImportData imports a package from the serialized package data |  | ||||||
| // and returns the number of bytes consumed and a reference to the package. |  | ||||||
| // If the export data version is not recognized or the format is otherwise |  | ||||||
| // compromised, an error is returned. |  | ||||||
| func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { |  | ||||||
| 	const currentVersion = 1 |  | ||||||
| 	version := int64(-1) |  | ||||||
| 	defer func() { |  | ||||||
| 		if e := recover(); e != nil { |  | ||||||
| 			if version > currentVersion { |  | ||||||
| 				err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) |  | ||||||
| 			} else { |  | ||||||
| 				err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	r := &intReader{bytes.NewReader(data), path} |  | ||||||
| 
 |  | ||||||
| 	version = int64(r.uint64()) |  | ||||||
| 	switch version { |  | ||||||
| 	case currentVersion, 0: |  | ||||||
| 	default: |  | ||||||
| 		errorf("unknown iexport format version %d", version) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	sLen := int64(r.uint64()) |  | ||||||
| 	dLen := int64(r.uint64()) |  | ||||||
| 
 |  | ||||||
| 	whence, _ := r.Seek(0, io.SeekCurrent) |  | ||||||
| 	stringData := data[whence : whence+sLen] |  | ||||||
| 	declData := data[whence+sLen : whence+sLen+dLen] |  | ||||||
| 	r.Seek(sLen+dLen, io.SeekCurrent) |  | ||||||
| 
 |  | ||||||
| 	p := iimporter{ |  | ||||||
| 		ipath:   path, |  | ||||||
| 		version: int(version), |  | ||||||
| 
 |  | ||||||
| 		stringData:  stringData, |  | ||||||
| 		stringCache: make(map[uint64]string), |  | ||||||
| 		pkgCache:    make(map[uint64]*types.Package), |  | ||||||
| 
 |  | ||||||
| 		declData: declData, |  | ||||||
| 		pkgIndex: make(map[*types.Package]map[string]uint64), |  | ||||||
| 		typCache: make(map[uint64]types.Type), |  | ||||||
| 
 |  | ||||||
| 		fake: fakeFileSet{ |  | ||||||
| 			fset:  fset, |  | ||||||
| 			files: make(map[string]*token.File), |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i, pt := range predeclared() { |  | ||||||
| 		p.typCache[uint64(i)] = pt |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	pkgList := make([]*types.Package, r.uint64()) |  | ||||||
| 	for i := range pkgList { |  | ||||||
| 		pkgPathOff := r.uint64() |  | ||||||
| 		pkgPath := p.stringAt(pkgPathOff) |  | ||||||
| 		pkgName := p.stringAt(r.uint64()) |  | ||||||
| 		_ = r.uint64() // package height; unused by go/types |  | ||||||
| 
 |  | ||||||
| 		if pkgPath == "" { |  | ||||||
| 			pkgPath = path |  | ||||||
| 		} |  | ||||||
| 		pkg := imports[pkgPath] |  | ||||||
| 		if pkg == nil { |  | ||||||
| 			pkg = types.NewPackage(pkgPath, pkgName) |  | ||||||
| 			imports[pkgPath] = pkg |  | ||||||
| 		} else if pkg.Name() != pkgName { |  | ||||||
| 			errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		p.pkgCache[pkgPathOff] = pkg |  | ||||||
| 
 |  | ||||||
| 		nameIndex := make(map[string]uint64) |  | ||||||
| 		for nSyms := r.uint64(); nSyms > 0; nSyms-- { |  | ||||||
| 			name := p.stringAt(r.uint64()) |  | ||||||
| 			nameIndex[name] = r.uint64() |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		p.pkgIndex[pkg] = nameIndex |  | ||||||
| 		pkgList[i] = pkg |  | ||||||
| 	} |  | ||||||
| 	if len(pkgList) == 0 { |  | ||||||
| 		errorf("no packages found for %s", path) |  | ||||||
| 		panic("unreachable") |  | ||||||
| 	} |  | ||||||
| 	p.ipkg = pkgList[0] |  | ||||||
| 	names := make([]string, 0, len(p.pkgIndex[p.ipkg])) |  | ||||||
| 	for name := range p.pkgIndex[p.ipkg] { |  | ||||||
| 		names = append(names, name) |  | ||||||
| 	} |  | ||||||
| 	sort.Strings(names) |  | ||||||
| 	for _, name := range names { |  | ||||||
| 		p.doDecl(p.ipkg, name) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for _, typ := range p.interfaceList { |  | ||||||
| 		typ.Complete() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// record all referenced packages as imports |  | ||||||
| 	list := append(([]*types.Package)(nil), pkgList[1:]...) |  | ||||||
| 	sort.Sort(byPath(list)) |  | ||||||
| 	p.ipkg.SetImports(list) |  | ||||||
| 
 |  | ||||||
| 	// package was imported completely and without errors |  | ||||||
| 	p.ipkg.MarkComplete() |  | ||||||
| 
 |  | ||||||
| 	consumed, _ := r.Seek(0, io.SeekCurrent) |  | ||||||
| 	return int(consumed), p.ipkg, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type iimporter struct { |  | ||||||
| 	ipath   string |  | ||||||
| 	ipkg    *types.Package |  | ||||||
| 	version int |  | ||||||
| 
 |  | ||||||
| 	stringData  []byte |  | ||||||
| 	stringCache map[uint64]string |  | ||||||
| 	pkgCache    map[uint64]*types.Package |  | ||||||
| 
 |  | ||||||
| 	declData []byte |  | ||||||
| 	pkgIndex map[*types.Package]map[string]uint64 |  | ||||||
| 	typCache map[uint64]types.Type |  | ||||||
| 
 |  | ||||||
| 	fake          fakeFileSet |  | ||||||
| 	interfaceList []*types.Interface |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iimporter) doDecl(pkg *types.Package, name string) { |  | ||||||
| 	// See if we've already imported this declaration. |  | ||||||
| 	if obj := pkg.Scope().Lookup(name); obj != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	off, ok := p.pkgIndex[pkg][name] |  | ||||||
| 	if !ok { |  | ||||||
| 		errorf("%v.%v not in index", pkg, name) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r := &importReader{p: p, currPkg: pkg} |  | ||||||
| 	r.declReader.Reset(p.declData[off:]) |  | ||||||
| 
 |  | ||||||
| 	r.obj(name) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iimporter) stringAt(off uint64) string { |  | ||||||
| 	if s, ok := p.stringCache[off]; ok { |  | ||||||
| 		return s |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	slen, n := binary.Uvarint(p.stringData[off:]) |  | ||||||
| 	if n <= 0 { |  | ||||||
| 		errorf("varint failed") |  | ||||||
| 	} |  | ||||||
| 	spos := off + uint64(n) |  | ||||||
| 	s := string(p.stringData[spos : spos+slen]) |  | ||||||
| 	p.stringCache[off] = s |  | ||||||
| 	return s |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iimporter) pkgAt(off uint64) *types.Package { |  | ||||||
| 	if pkg, ok := p.pkgCache[off]; ok { |  | ||||||
| 		return pkg |  | ||||||
| 	} |  | ||||||
| 	path := p.stringAt(off) |  | ||||||
| 	if path == p.ipath { |  | ||||||
| 		return p.ipkg |  | ||||||
| 	} |  | ||||||
| 	errorf("missing package %q in %q", path, p.ipath) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (p *iimporter) typAt(off uint64, base *types.Named) types.Type { |  | ||||||
| 	if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) { |  | ||||||
| 		return t |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if off < predeclReserved { |  | ||||||
| 		errorf("predeclared type missing from cache: %v", off) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	r := &importReader{p: p} |  | ||||||
| 	r.declReader.Reset(p.declData[off-predeclReserved:]) |  | ||||||
| 	t := r.doType(base) |  | ||||||
| 
 |  | ||||||
| 	if base == nil || !isInterface(t) { |  | ||||||
| 		p.typCache[off] = t |  | ||||||
| 	} |  | ||||||
| 	return t |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type importReader struct { |  | ||||||
| 	p          *iimporter |  | ||||||
| 	declReader bytes.Reader |  | ||||||
| 	currPkg    *types.Package |  | ||||||
| 	prevFile   string |  | ||||||
| 	prevLine   int64 |  | ||||||
| 	prevColumn int64 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) obj(name string) { |  | ||||||
| 	tag := r.byte() |  | ||||||
| 	pos := r.pos() |  | ||||||
| 
 |  | ||||||
| 	switch tag { |  | ||||||
| 	case 'A': |  | ||||||
| 		typ := r.typ() |  | ||||||
| 
 |  | ||||||
| 		r.declare(types.NewTypeName(pos, r.currPkg, name, typ)) |  | ||||||
| 
 |  | ||||||
| 	case 'C': |  | ||||||
| 		typ, val := r.value() |  | ||||||
| 
 |  | ||||||
| 		r.declare(types.NewConst(pos, r.currPkg, name, typ, val)) |  | ||||||
| 
 |  | ||||||
| 	case 'F': |  | ||||||
| 		sig := r.signature(nil) |  | ||||||
| 
 |  | ||||||
| 		r.declare(types.NewFunc(pos, r.currPkg, name, sig)) |  | ||||||
| 
 |  | ||||||
| 	case 'T': |  | ||||||
| 		// Types can be recursive. We need to setup a stub |  | ||||||
| 		// declaration before recursing. |  | ||||||
| 		obj := types.NewTypeName(pos, r.currPkg, name, nil) |  | ||||||
| 		named := types.NewNamed(obj, nil, nil) |  | ||||||
| 		r.declare(obj) |  | ||||||
| 
 |  | ||||||
| 		underlying := r.p.typAt(r.uint64(), named).Underlying() |  | ||||||
| 		named.SetUnderlying(underlying) |  | ||||||
| 
 |  | ||||||
| 		if !isInterface(underlying) { |  | ||||||
| 			for n := r.uint64(); n > 0; n-- { |  | ||||||
| 				mpos := r.pos() |  | ||||||
| 				mname := r.ident() |  | ||||||
| 				recv := r.param() |  | ||||||
| 				msig := r.signature(recv) |  | ||||||
| 
 |  | ||||||
| 				named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	case 'V': |  | ||||||
| 		typ := r.typ() |  | ||||||
| 
 |  | ||||||
| 		r.declare(types.NewVar(pos, r.currPkg, name, typ)) |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		errorf("unexpected tag: %v", tag) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) declare(obj types.Object) { |  | ||||||
| 	obj.Pkg().Scope().Insert(obj) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) value() (typ types.Type, val constant.Value) { |  | ||||||
| 	typ = r.typ() |  | ||||||
| 
 |  | ||||||
| 	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType { |  | ||||||
| 	case types.IsBoolean: |  | ||||||
| 		val = constant.MakeBool(r.bool()) |  | ||||||
| 
 |  | ||||||
| 	case types.IsString: |  | ||||||
| 		val = constant.MakeString(r.string()) |  | ||||||
| 
 |  | ||||||
| 	case types.IsInteger: |  | ||||||
| 		val = r.mpint(b) |  | ||||||
| 
 |  | ||||||
| 	case types.IsFloat: |  | ||||||
| 		val = r.mpfloat(b) |  | ||||||
| 
 |  | ||||||
| 	case types.IsComplex: |  | ||||||
| 		re := r.mpfloat(b) |  | ||||||
| 		im := r.mpfloat(b) |  | ||||||
| 		val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		if b.Kind() == types.Invalid { |  | ||||||
| 			val = constant.MakeUnknown() |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		errorf("unexpected type %v", typ) // panics |  | ||||||
| 		panic("unreachable") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func intSize(b *types.Basic) (signed bool, maxBytes uint) { |  | ||||||
| 	if (b.Info() & types.IsUntyped) != 0 { |  | ||||||
| 		return true, 64 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	switch b.Kind() { |  | ||||||
| 	case types.Float32, types.Complex64: |  | ||||||
| 		return true, 3 |  | ||||||
| 	case types.Float64, types.Complex128: |  | ||||||
| 		return true, 7 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	signed = (b.Info() & types.IsUnsigned) == 0 |  | ||||||
| 	switch b.Kind() { |  | ||||||
| 	case types.Int8, types.Uint8: |  | ||||||
| 		maxBytes = 1 |  | ||||||
| 	case types.Int16, types.Uint16: |  | ||||||
| 		maxBytes = 2 |  | ||||||
| 	case types.Int32, types.Uint32: |  | ||||||
| 		maxBytes = 4 |  | ||||||
| 	default: |  | ||||||
| 		maxBytes = 8 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) mpint(b *types.Basic) constant.Value { |  | ||||||
| 	signed, maxBytes := intSize(b) |  | ||||||
| 
 |  | ||||||
| 	maxSmall := 256 - maxBytes |  | ||||||
| 	if signed { |  | ||||||
| 		maxSmall = 256 - 2*maxBytes |  | ||||||
| 	} |  | ||||||
| 	if maxBytes == 1 { |  | ||||||
| 		maxSmall = 256 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	n, _ := r.declReader.ReadByte() |  | ||||||
| 	if uint(n) < maxSmall { |  | ||||||
| 		v := int64(n) |  | ||||||
| 		if signed { |  | ||||||
| 			v >>= 1 |  | ||||||
| 			if n&1 != 0 { |  | ||||||
| 				v = ^v |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return constant.MakeInt64(v) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	v := -n |  | ||||||
| 	if signed { |  | ||||||
| 		v = -(n &^ 1) >> 1 |  | ||||||
| 	} |  | ||||||
| 	if v < 1 || uint(v) > maxBytes { |  | ||||||
| 		errorf("weird decoding: %v, %v => %v", n, signed, v) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	buf := make([]byte, v) |  | ||||||
| 	io.ReadFull(&r.declReader, buf) |  | ||||||
| 
 |  | ||||||
| 	// convert to little endian |  | ||||||
| 	// TODO(gri) go/constant should have a more direct conversion function |  | ||||||
| 	//           (e.g., once it supports a big.Float based implementation) |  | ||||||
| 	for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { |  | ||||||
| 		buf[i], buf[j] = buf[j], buf[i] |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	x := constant.MakeFromBytes(buf) |  | ||||||
| 	if signed && n&1 != 0 { |  | ||||||
| 		x = constant.UnaryOp(token.SUB, x, 0) |  | ||||||
| 	} |  | ||||||
| 	return x |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) mpfloat(b *types.Basic) constant.Value { |  | ||||||
| 	x := r.mpint(b) |  | ||||||
| 	if constant.Sign(x) == 0 { |  | ||||||
| 		return x |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	exp := r.int64() |  | ||||||
| 	switch { |  | ||||||
| 	case exp > 0: |  | ||||||
| 		x = constant.Shift(x, token.SHL, uint(exp)) |  | ||||||
| 	case exp < 0: |  | ||||||
| 		d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) |  | ||||||
| 		x = constant.BinaryOp(x, token.QUO, d) |  | ||||||
| 	} |  | ||||||
| 	return x |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) ident() string { |  | ||||||
| 	return r.string() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) qualifiedIdent() (*types.Package, string) { |  | ||||||
| 	name := r.string() |  | ||||||
| 	pkg := r.pkg() |  | ||||||
| 	return pkg, name |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) pos() token.Pos { |  | ||||||
| 	if r.p.version >= 1 { |  | ||||||
| 		r.posv1() |  | ||||||
| 	} else { |  | ||||||
| 		r.posv0() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 { |  | ||||||
| 		return token.NoPos |  | ||||||
| 	} |  | ||||||
| 	return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) posv0() { |  | ||||||
| 	delta := r.int64() |  | ||||||
| 	if delta != deltaNewFile { |  | ||||||
| 		r.prevLine += delta |  | ||||||
| 	} else if l := r.int64(); l == -1 { |  | ||||||
| 		r.prevLine += deltaNewFile |  | ||||||
| 	} else { |  | ||||||
| 		r.prevFile = r.string() |  | ||||||
| 		r.prevLine = l |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) posv1() { |  | ||||||
| 	delta := r.int64() |  | ||||||
| 	r.prevColumn += delta >> 1 |  | ||||||
| 	if delta&1 != 0 { |  | ||||||
| 		delta = r.int64() |  | ||||||
| 		r.prevLine += delta >> 1 |  | ||||||
| 		if delta&1 != 0 { |  | ||||||
| 			r.prevFile = r.string() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) typ() types.Type { |  | ||||||
| 	return r.p.typAt(r.uint64(), nil) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isInterface(t types.Type) bool { |  | ||||||
| 	_, ok := t.(*types.Interface) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) } |  | ||||||
| func (r *importReader) string() string      { return r.p.stringAt(r.uint64()) } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) doType(base *types.Named) types.Type { |  | ||||||
| 	switch k := r.kind(); k { |  | ||||||
| 	default: |  | ||||||
| 		errorf("unexpected kind tag in %q: %v", r.p.ipath, k) |  | ||||||
| 		return nil |  | ||||||
| 
 |  | ||||||
| 	case definedType: |  | ||||||
| 		pkg, name := r.qualifiedIdent() |  | ||||||
| 		r.p.doDecl(pkg, name) |  | ||||||
| 		return pkg.Scope().Lookup(name).(*types.TypeName).Type() |  | ||||||
| 	case pointerType: |  | ||||||
| 		return types.NewPointer(r.typ()) |  | ||||||
| 	case sliceType: |  | ||||||
| 		return types.NewSlice(r.typ()) |  | ||||||
| 	case arrayType: |  | ||||||
| 		n := r.uint64() |  | ||||||
| 		return types.NewArray(r.typ(), int64(n)) |  | ||||||
| 	case chanType: |  | ||||||
| 		dir := chanDir(int(r.uint64())) |  | ||||||
| 		return types.NewChan(dir, r.typ()) |  | ||||||
| 	case mapType: |  | ||||||
| 		return types.NewMap(r.typ(), r.typ()) |  | ||||||
| 	case signatureType: |  | ||||||
| 		r.currPkg = r.pkg() |  | ||||||
| 		return r.signature(nil) |  | ||||||
| 
 |  | ||||||
| 	case structType: |  | ||||||
| 		r.currPkg = r.pkg() |  | ||||||
| 
 |  | ||||||
| 		fields := make([]*types.Var, r.uint64()) |  | ||||||
| 		tags := make([]string, len(fields)) |  | ||||||
| 		for i := range fields { |  | ||||||
| 			fpos := r.pos() |  | ||||||
| 			fname := r.ident() |  | ||||||
| 			ftyp := r.typ() |  | ||||||
| 			emb := r.bool() |  | ||||||
| 			tag := r.string() |  | ||||||
| 
 |  | ||||||
| 			fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) |  | ||||||
| 			tags[i] = tag |  | ||||||
| 		} |  | ||||||
| 		return types.NewStruct(fields, tags) |  | ||||||
| 
 |  | ||||||
| 	case interfaceType: |  | ||||||
| 		r.currPkg = r.pkg() |  | ||||||
| 
 |  | ||||||
| 		embeddeds := make([]types.Type, r.uint64()) |  | ||||||
| 		for i := range embeddeds { |  | ||||||
| 			_ = r.pos() |  | ||||||
| 			embeddeds[i] = r.typ() |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		methods := make([]*types.Func, r.uint64()) |  | ||||||
| 		for i := range methods { |  | ||||||
| 			mpos := r.pos() |  | ||||||
| 			mname := r.ident() |  | ||||||
| 
 |  | ||||||
| 			// TODO(mdempsky): Matches bimport.go, but I |  | ||||||
| 			// don't agree with this. |  | ||||||
| 			var recv *types.Var |  | ||||||
| 			if base != nil { |  | ||||||
| 				recv = types.NewVar(token.NoPos, r.currPkg, "", base) |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			msig := r.signature(recv) |  | ||||||
| 			methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		typ := newInterface(methods, embeddeds) |  | ||||||
| 		r.p.interfaceList = append(r.p.interfaceList, typ) |  | ||||||
| 		return typ |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) kind() itag { |  | ||||||
| 	return itag(r.uint64()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) signature(recv *types.Var) *types.Signature { |  | ||||||
| 	params := r.paramList() |  | ||||||
| 	results := r.paramList() |  | ||||||
| 	variadic := params.Len() > 0 && r.bool() |  | ||||||
| 	return types.NewSignature(recv, params, results, variadic) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) paramList() *types.Tuple { |  | ||||||
| 	xs := make([]*types.Var, r.uint64()) |  | ||||||
| 	for i := range xs { |  | ||||||
| 		xs[i] = r.param() |  | ||||||
| 	} |  | ||||||
| 	return types.NewTuple(xs...) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) param() *types.Var { |  | ||||||
| 	pos := r.pos() |  | ||||||
| 	name := r.ident() |  | ||||||
| 	typ := r.typ() |  | ||||||
| 	return types.NewParam(pos, r.currPkg, name, typ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) bool() bool { |  | ||||||
| 	return r.uint64() != 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) int64() int64 { |  | ||||||
| 	n, err := binary.ReadVarint(&r.declReader) |  | ||||||
| 	if err != nil { |  | ||||||
| 		errorf("readVarint: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return n |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) uint64() uint64 { |  | ||||||
| 	n, err := binary.ReadUvarint(&r.declReader) |  | ||||||
| 	if err != nil { |  | ||||||
| 		errorf("readUvarint: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return n |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (r *importReader) byte() byte { |  | ||||||
| 	x, err := r.declReader.ReadByte() |  | ||||||
| 	if err != nil { |  | ||||||
| 		errorf("declReader.ReadByte: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return x |  | ||||||
| } |  | ||||||
							
								
								
									
										21
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,21 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // +build !go1.11 |  | ||||||
| 
 |  | ||||||
| package gcimporter |  | ||||||
| 
 |  | ||||||
| import "go/types" |  | ||||||
| 
 |  | ||||||
| func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { |  | ||||||
| 	named := make([]*types.Named, len(embeddeds)) |  | ||||||
| 	for i, e := range embeddeds { |  | ||||||
| 		var ok bool |  | ||||||
| 		named[i], ok = e.(*types.Named) |  | ||||||
| 		if !ok { |  | ||||||
| 			panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return types.NewInterface(methods, named) |  | ||||||
| } |  | ||||||
							
								
								
									
										13
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,13 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // +build go1.11 |  | ||||||
| 
 |  | ||||||
| package gcimporter |  | ||||||
| 
 |  | ||||||
| import "go/types" |  | ||||||
| 
 |  | ||||||
| func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface { |  | ||||||
| 	return types.NewInterfaceType(methods, embeddeds) |  | ||||||
| } |  | ||||||
							
								
								
									
										117
									
								
								vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,117 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package packagesdriver fetches type sizes for go/packages and go/analysis. |  | ||||||
| package packagesdriver |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"context" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/types" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/gocommand" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var debug = false |  | ||||||
| 
 |  | ||||||
| func GetSizes(ctx context.Context, buildFlags, env []string, gocmdRunner *gocommand.Runner, dir string) (types.Sizes, error) { |  | ||||||
| 	// TODO(matloob): Clean this up. This code is mostly a copy of packages.findExternalDriver. |  | ||||||
| 	const toolPrefix = "GOPACKAGESDRIVER=" |  | ||||||
| 	tool := "" |  | ||||||
| 	for _, env := range env { |  | ||||||
| 		if val := strings.TrimPrefix(env, toolPrefix); val != env { |  | ||||||
| 			tool = val |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if tool == "" { |  | ||||||
| 		var err error |  | ||||||
| 		tool, err = exec.LookPath("gopackagesdriver") |  | ||||||
| 		if err != nil { |  | ||||||
| 			// We did not find the driver, so use "go list". |  | ||||||
| 			tool = "off" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if tool == "off" { |  | ||||||
| 		return GetSizesGolist(ctx, buildFlags, env, gocmdRunner, dir) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	req, err := json.Marshal(struct { |  | ||||||
| 		Command    string   `json:"command"` |  | ||||||
| 		Env        []string `json:"env"` |  | ||||||
| 		BuildFlags []string `json:"build_flags"` |  | ||||||
| 	}{ |  | ||||||
| 		Command:    "sizes", |  | ||||||
| 		Env:        env, |  | ||||||
| 		BuildFlags: buildFlags, |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	buf := new(bytes.Buffer) |  | ||||||
| 	cmd := exec.CommandContext(ctx, tool) |  | ||||||
| 	cmd.Dir = dir |  | ||||||
| 	cmd.Env = env |  | ||||||
| 	cmd.Stdin = bytes.NewReader(req) |  | ||||||
| 	cmd.Stdout = buf |  | ||||||
| 	cmd.Stderr = new(bytes.Buffer) |  | ||||||
| 	if err := cmd.Run(); err != nil { |  | ||||||
| 		return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) |  | ||||||
| 	} |  | ||||||
| 	var response struct { |  | ||||||
| 		// Sizes, if not nil, is the types.Sizes to use when type checking. |  | ||||||
| 		Sizes *types.StdSizes |  | ||||||
| 	} |  | ||||||
| 	if err := json.Unmarshal(buf.Bytes(), &response); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return response.Sizes, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func GetSizesGolist(ctx context.Context, buildFlags, env []string, gocmdRunner *gocommand.Runner, dir string) (types.Sizes, error) { |  | ||||||
| 	inv := gocommand.Invocation{ |  | ||||||
| 		Verb:       "list", |  | ||||||
| 		Args:       []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}, |  | ||||||
| 		Env:        env, |  | ||||||
| 		BuildFlags: buildFlags, |  | ||||||
| 		WorkingDir: dir, |  | ||||||
| 	} |  | ||||||
| 	stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) |  | ||||||
| 	var goarch, compiler string |  | ||||||
| 	if rawErr != nil { |  | ||||||
| 		if strings.Contains(rawErr.Error(), "cannot find main module") { |  | ||||||
| 			// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. |  | ||||||
| 			// TODO(matloob): Is this a problem in practice? |  | ||||||
| 			inv := gocommand.Invocation{ |  | ||||||
| 				Verb:       "env", |  | ||||||
| 				Args:       []string{"GOARCH"}, |  | ||||||
| 				Env:        env, |  | ||||||
| 				WorkingDir: dir, |  | ||||||
| 			} |  | ||||||
| 			envout, enverr := gocmdRunner.Run(ctx, inv) |  | ||||||
| 			if enverr != nil { |  | ||||||
| 				return nil, enverr |  | ||||||
| 			} |  | ||||||
| 			goarch = strings.TrimSpace(envout.String()) |  | ||||||
| 			compiler = "gc" |  | ||||||
| 		} else { |  | ||||||
| 			return nil, friendlyErr |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		fields := strings.Fields(stdout.String()) |  | ||||||
| 		if len(fields) < 2 { |  | ||||||
| 			return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>", |  | ||||||
| 				stdout.String(), stderr.String()) |  | ||||||
| 		} |  | ||||||
| 		goarch = fields[0] |  | ||||||
| 		compiler = fields[1] |  | ||||||
| 	} |  | ||||||
| 	return types.SizesFor(compiler, goarch), nil |  | ||||||
| } |  | ||||||
							
								
								
									
										221
									
								
								vendor/golang.org/x/tools/go/packages/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										221
									
								
								vendor/golang.org/x/tools/go/packages/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,221 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| Package packages loads Go packages for inspection and analysis. |  | ||||||
| 
 |  | ||||||
| The Load function takes as input a list of patterns and return a list of Package |  | ||||||
| structs describing individual packages matched by those patterns. |  | ||||||
| The LoadMode controls the amount of detail in the loaded packages. |  | ||||||
| 
 |  | ||||||
| Load passes most patterns directly to the underlying build tool, |  | ||||||
| but all patterns with the prefix "query=", where query is a |  | ||||||
| non-empty string of letters from [a-z], are reserved and may be |  | ||||||
| interpreted as query operators. |  | ||||||
| 
 |  | ||||||
| Two query operators are currently supported: "file" and "pattern". |  | ||||||
| 
 |  | ||||||
| The query "file=path/to/file.go" matches the package or packages enclosing |  | ||||||
| the Go source file path/to/file.go.  For example "file=~/go/src/fmt/print.go" |  | ||||||
| might return the packages "fmt" and "fmt [fmt.test]". |  | ||||||
| 
 |  | ||||||
| The query "pattern=string" causes "string" to be passed directly to |  | ||||||
| the underlying build tool. In most cases this is unnecessary, |  | ||||||
| but an application can use Load("pattern=" + x) as an escaping mechanism |  | ||||||
| to ensure that x is not interpreted as a query operator if it contains '='. |  | ||||||
| 
 |  | ||||||
| All other query operators are reserved for future use and currently |  | ||||||
| cause Load to report an error. |  | ||||||
| 
 |  | ||||||
| The Package struct provides basic information about the package, including |  | ||||||
| 
 |  | ||||||
|   - ID, a unique identifier for the package in the returned set; |  | ||||||
|   - GoFiles, the names of the package's Go source files; |  | ||||||
|   - Imports, a map from source import strings to the Packages they name; |  | ||||||
|   - Types, the type information for the package's exported symbols; |  | ||||||
|   - Syntax, the parsed syntax trees for the package's source code; and |  | ||||||
|   - TypeInfo, the result of a complete type-check of the package syntax trees. |  | ||||||
| 
 |  | ||||||
| (See the documentation for type Package for the complete list of fields |  | ||||||
| and more detailed descriptions.) |  | ||||||
| 
 |  | ||||||
| For example, |  | ||||||
| 
 |  | ||||||
| 	Load(nil, "bytes", "unicode...") |  | ||||||
| 
 |  | ||||||
| returns four Package structs describing the standard library packages |  | ||||||
| bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern |  | ||||||
| can match multiple packages and that a package might be matched by |  | ||||||
| multiple patterns: in general it is not possible to determine which |  | ||||||
| packages correspond to which patterns. |  | ||||||
| 
 |  | ||||||
| Note that the list returned by Load contains only the packages matched |  | ||||||
| by the patterns. Their dependencies can be found by walking the import |  | ||||||
| graph using the Imports fields. |  | ||||||
| 
 |  | ||||||
| The Load function can be configured by passing a pointer to a Config as |  | ||||||
| the first argument. A nil Config is equivalent to the zero Config, which |  | ||||||
| causes Load to run in LoadFiles mode, collecting minimal information. |  | ||||||
| See the documentation for type Config for details. |  | ||||||
| 
 |  | ||||||
| As noted earlier, the Config.Mode controls the amount of detail |  | ||||||
| reported about the loaded packages. See the documentation for type LoadMode |  | ||||||
| for details. |  | ||||||
| 
 |  | ||||||
| Most tools should pass their command-line arguments (after any flags) |  | ||||||
| uninterpreted to the loader, so that the loader can interpret them |  | ||||||
| according to the conventions of the underlying build system. |  | ||||||
| See the Example function for typical usage. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| package packages // import "golang.org/x/tools/go/packages" |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| 
 |  | ||||||
| Motivation and design considerations |  | ||||||
| 
 |  | ||||||
| The new package's design solves problems addressed by two existing |  | ||||||
| packages: go/build, which locates and describes packages, and |  | ||||||
| golang.org/x/tools/go/loader, which loads, parses and type-checks them. |  | ||||||
| The go/build.Package structure encodes too much of the 'go build' way |  | ||||||
| of organizing projects, leaving us in need of a data type that describes a |  | ||||||
| package of Go source code independent of the underlying build system. |  | ||||||
| We wanted something that works equally well with go build and vgo, and |  | ||||||
| also other build systems such as Bazel and Blaze, making it possible to |  | ||||||
| construct analysis tools that work in all these environments. |  | ||||||
| Tools such as errcheck and staticcheck were essentially unavailable to |  | ||||||
| the Go community at Google, and some of Google's internal tools for Go |  | ||||||
| are unavailable externally. |  | ||||||
| This new package provides a uniform way to obtain package metadata by |  | ||||||
| querying each of these build systems, optionally supporting their |  | ||||||
| preferred command-line notations for packages, so that tools integrate |  | ||||||
| neatly with users' build environments. The Metadata query function |  | ||||||
| executes an external query tool appropriate to the current workspace. |  | ||||||
| 
 |  | ||||||
| Loading packages always returns the complete import graph "all the way down", |  | ||||||
| even if all you want is information about a single package, because the query |  | ||||||
| mechanisms of all the build systems we currently support ({go,vgo} list, and |  | ||||||
| blaze/bazel aspect-based query) cannot provide detailed information |  | ||||||
| about one package without visiting all its dependencies too, so there is |  | ||||||
| no additional asymptotic cost to providing transitive information. |  | ||||||
| (This property might not be true of a hypothetical 5th build system.) |  | ||||||
| 
 |  | ||||||
| In calls to TypeCheck, all initial packages, and any package that |  | ||||||
| transitively depends on one of them, must be loaded from source. |  | ||||||
| Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from |  | ||||||
| source; D may be loaded from export data, and E may not be loaded at all |  | ||||||
| (though it's possible that D's export data mentions it, so a |  | ||||||
| types.Package may be created for it and exposed.) |  | ||||||
| 
 |  | ||||||
| The old loader had a feature to suppress type-checking of function |  | ||||||
| bodies on a per-package basis, primarily intended to reduce the work of |  | ||||||
| obtaining type information for imported packages. Now that imports are |  | ||||||
| satisfied by export data, the optimization no longer seems necessary. |  | ||||||
| 
 |  | ||||||
| Despite some early attempts, the old loader did not exploit export data, |  | ||||||
| instead always using the equivalent of WholeProgram mode. This was due |  | ||||||
| to the complexity of mixing source and export data packages (now |  | ||||||
| resolved by the upward traversal mentioned above), and because export data |  | ||||||
| files were nearly always missing or stale. Now that 'go build' supports |  | ||||||
| caching, all the underlying build systems can guarantee to produce |  | ||||||
| export data in a reasonable (amortized) time. |  | ||||||
| 
 |  | ||||||
| Test "main" packages synthesized by the build system are now reported as |  | ||||||
| first-class packages, avoiding the need for clients (such as go/ssa) to |  | ||||||
| reinvent this generation logic. |  | ||||||
| 
 |  | ||||||
| One way in which go/packages is simpler than the old loader is in its |  | ||||||
| treatment of in-package tests. In-package tests are packages that |  | ||||||
| consist of all the files of the library under test, plus the test files. |  | ||||||
| The old loader constructed in-package tests by a two-phase process of |  | ||||||
| mutation called "augmentation": first it would construct and type check |  | ||||||
| all the ordinary library packages and type-check the packages that |  | ||||||
| depend on them; then it would add more (test) files to the package and |  | ||||||
| type-check again. This two-phase approach had four major problems: |  | ||||||
| 1) in processing the tests, the loader modified the library package, |  | ||||||
|    leaving no way for a client application to see both the test |  | ||||||
|    package and the library package; one would mutate into the other. |  | ||||||
| 2) because test files can declare additional methods on types defined in |  | ||||||
|    the library portion of the package, the dispatch of method calls in |  | ||||||
|    the library portion was affected by the presence of the test files. |  | ||||||
|    This should have been a clue that the packages were logically |  | ||||||
|    different. |  | ||||||
| 3) this model of "augmentation" assumed at most one in-package test |  | ||||||
|    per library package, which is true of projects using 'go build', |  | ||||||
|    but not other build systems. |  | ||||||
| 4) because of the two-phase nature of test processing, all packages that |  | ||||||
|    import the library package had to be processed before augmentation, |  | ||||||
|    forcing a "one-shot" API and preventing the client from calling Load |  | ||||||
|    in several times in sequence as is now possible in WholeProgram mode. |  | ||||||
|    (TypeCheck mode has a similar one-shot restriction for a different reason.) |  | ||||||
| 
 |  | ||||||
| Early drafts of this package supported "multi-shot" operation. |  | ||||||
| Although it allowed clients to make a sequence of calls (or concurrent |  | ||||||
| calls) to Load, building up the graph of Packages incrementally, |  | ||||||
| it was of marginal value: it complicated the API |  | ||||||
| (since it allowed some options to vary across calls but not others), |  | ||||||
| it complicated the implementation, |  | ||||||
| it cannot be made to work in Types mode, as explained above, |  | ||||||
| and it was less efficient than making one combined call (when this is possible). |  | ||||||
| Among the clients we have inspected, none made multiple calls to load |  | ||||||
| but could not be easily and satisfactorily modified to make only a single call. |  | ||||||
| However, applications changes may be required. |  | ||||||
| For example, the ssadump command loads the user-specified packages |  | ||||||
| and in addition the runtime package.  It is tempting to simply append |  | ||||||
| "runtime" to the user-provided list, but that does not work if the user |  | ||||||
| specified an ad-hoc package such as [a.go b.go]. |  | ||||||
| Instead, ssadump no longer requests the runtime package, |  | ||||||
| but seeks it among the dependencies of the user-specified packages, |  | ||||||
| and emits an error if it is not found. |  | ||||||
| 
 |  | ||||||
| Overlays: The Overlay field in the Config allows providing alternate contents |  | ||||||
| for Go source files, by providing a mapping from file path to contents. |  | ||||||
| go/packages will pull in new imports added in overlay files when go/packages |  | ||||||
| is run in LoadImports mode or greater. |  | ||||||
| Overlay support for the go list driver isn't complete yet: if the file doesn't |  | ||||||
| exist on disk, it will only be recognized in an overlay if it is a non-test file |  | ||||||
| and the package would be reported even without the overlay. |  | ||||||
| 
 |  | ||||||
| Questions & Tasks |  | ||||||
| 
 |  | ||||||
| - Add GOARCH/GOOS? |  | ||||||
|   They are not portable concepts, but could be made portable. |  | ||||||
|   Our goal has been to allow users to express themselves using the conventions |  | ||||||
|   of the underlying build system: if the build system honors GOARCH |  | ||||||
|   during a build and during a metadata query, then so should |  | ||||||
|   applications built atop that query mechanism. |  | ||||||
|   Conversely, if the target architecture of the build is determined by |  | ||||||
|   command-line flags, the application can pass the relevant |  | ||||||
|   flags through to the build system using a command such as: |  | ||||||
|     myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin" |  | ||||||
|   However, this approach is low-level, unwieldy, and non-portable. |  | ||||||
|   GOOS and GOARCH seem important enough to warrant a dedicated option. |  | ||||||
| 
 |  | ||||||
| - How should we handle partial failures such as a mixture of good and |  | ||||||
|   malformed patterns, existing and non-existent packages, successful and |  | ||||||
|   failed builds, import failures, import cycles, and so on, in a call to |  | ||||||
|   Load? |  | ||||||
| 
 |  | ||||||
| - Support bazel, blaze, and go1.10 list, not just go1.11 list. |  | ||||||
| 
 |  | ||||||
| - Handle (and test) various partial success cases, e.g. |  | ||||||
|   a mixture of good packages and: |  | ||||||
|   invalid patterns |  | ||||||
|   nonexistent packages |  | ||||||
|   empty packages |  | ||||||
|   packages with malformed package or import declarations |  | ||||||
|   unreadable files |  | ||||||
|   import cycles |  | ||||||
|   other parse errors |  | ||||||
|   type errors |  | ||||||
|   Make sure we record errors at the correct place in the graph. |  | ||||||
| 
 |  | ||||||
| - Missing packages among initial arguments are not reported. |  | ||||||
|   Return bogus packages for them, like golist does. |  | ||||||
| 
 |  | ||||||
| - "undeclared name" errors (for example) are reported out of source file |  | ||||||
|   order. I suspect this is due to the breadth-first resolution now used |  | ||||||
|   by go/types. Is that a bug? Discuss with gri. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
							
								
								
									
										101
									
								
								vendor/golang.org/x/tools/go/packages/external.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										101
									
								
								vendor/golang.org/x/tools/go/packages/external.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,101 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // This file enables an external tool to intercept package requests. |  | ||||||
| // If the tool is present then its results are used in preference to |  | ||||||
| // the go list command. |  | ||||||
| 
 |  | ||||||
| package packages |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // The Driver Protocol |  | ||||||
| // |  | ||||||
| // The driver, given the inputs to a call to Load, returns metadata about the packages specified. |  | ||||||
| // This allows for different build systems to support go/packages by telling go/packages how the |  | ||||||
| // packages' source is organized. |  | ||||||
| // The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in |  | ||||||
| // the path as gopackagesdriver. It's given the inputs to load in its argv. See the package |  | ||||||
| // documentation in doc.go for the full description of the patterns that need to be supported. |  | ||||||
| // A driver receives as a JSON-serialized driverRequest struct in standard input and will |  | ||||||
| // produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output. |  | ||||||
| 
 |  | ||||||
| // driverRequest is used to provide the portion of Load's Config that is needed by a driver. |  | ||||||
| type driverRequest struct { |  | ||||||
| 	Mode LoadMode `json:"mode"` |  | ||||||
| 	// Env specifies the environment the underlying build system should be run in. |  | ||||||
| 	Env []string `json:"env"` |  | ||||||
| 	// BuildFlags are flags that should be passed to the underlying build system. |  | ||||||
| 	BuildFlags []string `json:"build_flags"` |  | ||||||
| 	// Tests specifies whether the patterns should also return test packages. |  | ||||||
| 	Tests bool `json:"tests"` |  | ||||||
| 	// Overlay maps file paths (relative to the driver's working directory) to the byte contents |  | ||||||
| 	// of overlay files. |  | ||||||
| 	Overlay map[string][]byte `json:"overlay"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // findExternalDriver returns the file path of a tool that supplies |  | ||||||
| // the build system package structure, or "" if not found." |  | ||||||
| // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its |  | ||||||
| // value, otherwise it searches for a binary named gopackagesdriver on the PATH. |  | ||||||
| func findExternalDriver(cfg *Config) driver { |  | ||||||
| 	const toolPrefix = "GOPACKAGESDRIVER=" |  | ||||||
| 	tool := "" |  | ||||||
| 	for _, env := range cfg.Env { |  | ||||||
| 		if val := strings.TrimPrefix(env, toolPrefix); val != env { |  | ||||||
| 			tool = val |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if tool != "" && tool == "off" { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	if tool == "" { |  | ||||||
| 		var err error |  | ||||||
| 		tool, err = exec.LookPath("gopackagesdriver") |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return func(cfg *Config, words ...string) (*driverResponse, error) { |  | ||||||
| 		req, err := json.Marshal(driverRequest{ |  | ||||||
| 			Mode:       cfg.Mode, |  | ||||||
| 			Env:        cfg.Env, |  | ||||||
| 			BuildFlags: cfg.BuildFlags, |  | ||||||
| 			Tests:      cfg.Tests, |  | ||||||
| 			Overlay:    cfg.Overlay, |  | ||||||
| 		}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, fmt.Errorf("failed to encode message to driver tool: %v", err) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		buf := new(bytes.Buffer) |  | ||||||
| 		stderr := new(bytes.Buffer) |  | ||||||
| 		cmd := exec.CommandContext(cfg.Context, tool, words...) |  | ||||||
| 		cmd.Dir = cfg.Dir |  | ||||||
| 		cmd.Env = cfg.Env |  | ||||||
| 		cmd.Stdin = bytes.NewReader(req) |  | ||||||
| 		cmd.Stdout = buf |  | ||||||
| 		cmd.Stderr = stderr |  | ||||||
| 
 |  | ||||||
| 		if err := cmd.Run(); err != nil { |  | ||||||
| 			return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr) |  | ||||||
| 		} |  | ||||||
| 		if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" { |  | ||||||
| 			fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, words...), stderr) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var response driverResponse |  | ||||||
| 		if err := json.Unmarshal(buf.Bytes(), &response); err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		return &response, nil |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										1012
									
								
								vendor/golang.org/x/tools/go/packages/golist.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1012
									
								
								vendor/golang.org/x/tools/go/packages/golist.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										568
									
								
								vendor/golang.org/x/tools/go/packages/golist_overlay.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										568
									
								
								vendor/golang.org/x/tools/go/packages/golist_overlay.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,568 +0,0 @@ | |||||||
| package packages |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"go/parser" |  | ||||||
| 	"go/token" |  | ||||||
| 	"log" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"regexp" |  | ||||||
| 	"sort" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/gocommand" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // processGolistOverlay provides rudimentary support for adding |  | ||||||
| // files that don't exist on disk to an overlay. The results can be |  | ||||||
| // sometimes incorrect. |  | ||||||
| // TODO(matloob): Handle unsupported cases, including the following: |  | ||||||
| // - determining the correct package to add given a new import path |  | ||||||
| func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { |  | ||||||
| 	havePkgs := make(map[string]string) // importPath -> non-test package ID |  | ||||||
| 	needPkgsSet := make(map[string]bool) |  | ||||||
| 	modifiedPkgsSet := make(map[string]bool) |  | ||||||
| 
 |  | ||||||
| 	pkgOfDir := make(map[string][]*Package) |  | ||||||
| 	for _, pkg := range response.dr.Packages { |  | ||||||
| 		// This is an approximation of import path to id. This can be |  | ||||||
| 		// wrong for tests, vendored packages, and a number of other cases. |  | ||||||
| 		havePkgs[pkg.PkgPath] = pkg.ID |  | ||||||
| 		x := commonDir(pkg.GoFiles) |  | ||||||
| 		if x != "" { |  | ||||||
| 			pkgOfDir[x] = append(pkgOfDir[x], pkg) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// If no new imports are added, it is safe to avoid loading any needPkgs. |  | ||||||
| 	// Otherwise, it's hard to tell which package is actually being loaded |  | ||||||
| 	// (due to vendoring) and whether any modified package will show up |  | ||||||
| 	// in the transitive set of dependencies (because new imports are added, |  | ||||||
| 	// potentially modifying the transitive set of dependencies). |  | ||||||
| 	var overlayAddsImports bool |  | ||||||
| 
 |  | ||||||
| 	// If both a package and its test package are created by the overlay, we |  | ||||||
| 	// need the real package first. Process all non-test files before test |  | ||||||
| 	// files, and make the whole process deterministic while we're at it. |  | ||||||
| 	var overlayFiles []string |  | ||||||
| 	for opath := range state.cfg.Overlay { |  | ||||||
| 		overlayFiles = append(overlayFiles, opath) |  | ||||||
| 	} |  | ||||||
| 	sort.Slice(overlayFiles, func(i, j int) bool { |  | ||||||
| 		iTest := strings.HasSuffix(overlayFiles[i], "_test.go") |  | ||||||
| 		jTest := strings.HasSuffix(overlayFiles[j], "_test.go") |  | ||||||
| 		if iTest != jTest { |  | ||||||
| 			return !iTest // non-tests are before tests. |  | ||||||
| 		} |  | ||||||
| 		return overlayFiles[i] < overlayFiles[j] |  | ||||||
| 	}) |  | ||||||
| 	for _, opath := range overlayFiles { |  | ||||||
| 		contents := state.cfg.Overlay[opath] |  | ||||||
| 		base := filepath.Base(opath) |  | ||||||
| 		dir := filepath.Dir(opath) |  | ||||||
| 		var pkg *Package           // if opath belongs to both a package and its test variant, this will be the test variant |  | ||||||
| 		var testVariantOf *Package // if opath is a test file, this is the package it is testing |  | ||||||
| 		var fileExists bool |  | ||||||
| 		isTestFile := strings.HasSuffix(opath, "_test.go") |  | ||||||
| 		pkgName, ok := extractPackageName(opath, contents) |  | ||||||
| 		if !ok { |  | ||||||
| 			// Don't bother adding a file that doesn't even have a parsable package statement |  | ||||||
| 			// to the overlay. |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		// If all the overlay files belong to a different package, change the |  | ||||||
| 		// package name to that package. |  | ||||||
| 		maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir]) |  | ||||||
| 	nextPackage: |  | ||||||
| 		for _, p := range response.dr.Packages { |  | ||||||
| 			if pkgName != p.Name && p.ID != "command-line-arguments" { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			for _, f := range p.GoFiles { |  | ||||||
| 				if !sameFile(filepath.Dir(f), dir) { |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 				// Make sure to capture information on the package's test variant, if needed. |  | ||||||
| 				if isTestFile && !hasTestFiles(p) { |  | ||||||
| 					// TODO(matloob): Are there packages other than the 'production' variant |  | ||||||
| 					// of a package that this can match? This shouldn't match the test main package |  | ||||||
| 					// because the file is generated in another directory. |  | ||||||
| 					testVariantOf = p |  | ||||||
| 					continue nextPackage |  | ||||||
| 				} else if !isTestFile && hasTestFiles(p) { |  | ||||||
| 					// We're examining a test variant, but the overlaid file is |  | ||||||
| 					// a non-test file. Because the overlay implementation |  | ||||||
| 					// (currently) only adds a file to one package, skip this |  | ||||||
| 					// package, so that we can add the file to the production |  | ||||||
| 					// variant of the package. (https://golang.org/issue/36857 |  | ||||||
| 					// tracks handling overlays on both the production and test |  | ||||||
| 					// variant of a package). |  | ||||||
| 					continue nextPackage |  | ||||||
| 				} |  | ||||||
| 				if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { |  | ||||||
| 					// We have already seen the production version of the |  | ||||||
| 					// for which p is a test variant. |  | ||||||
| 					if hasTestFiles(p) { |  | ||||||
| 						testVariantOf = pkg |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				pkg = p |  | ||||||
| 				if filepath.Base(f) == base { |  | ||||||
| 					fileExists = true |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		// The overlay could have included an entirely new package or an |  | ||||||
| 		// ad-hoc package. An ad-hoc package is one that we have manually |  | ||||||
| 		// constructed from inadequate `go list` results for a file= query. |  | ||||||
| 		// It will have the ID command-line-arguments. |  | ||||||
| 		if pkg == nil || pkg.ID == "command-line-arguments" { |  | ||||||
| 			// Try to find the module or gopath dir the file is contained in. |  | ||||||
| 			// Then for modules, add the module opath to the beginning. |  | ||||||
| 			pkgPath, ok, err := state.getPkgPath(dir) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, nil, err |  | ||||||
| 			} |  | ||||||
| 			if !ok { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			var forTest string // only set for x tests |  | ||||||
| 			isXTest := strings.HasSuffix(pkgName, "_test") |  | ||||||
| 			if isXTest { |  | ||||||
| 				forTest = pkgPath |  | ||||||
| 				pkgPath += "_test" |  | ||||||
| 			} |  | ||||||
| 			id := pkgPath |  | ||||||
| 			if isTestFile { |  | ||||||
| 				if isXTest { |  | ||||||
| 					id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest) |  | ||||||
| 				} else { |  | ||||||
| 					id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if pkg != nil { |  | ||||||
| 				// TODO(rstambler): We should change the package's path and ID |  | ||||||
| 				// here. The only issue is that this messes with the roots. |  | ||||||
| 			} else { |  | ||||||
| 				// Try to reclaim a package with the same ID, if it exists in the response. |  | ||||||
| 				for _, p := range response.dr.Packages { |  | ||||||
| 					if reclaimPackage(p, id, opath, contents) { |  | ||||||
| 						pkg = p |  | ||||||
| 						break |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				// Otherwise, create a new package. |  | ||||||
| 				if pkg == nil { |  | ||||||
| 					pkg = &Package{ |  | ||||||
| 						PkgPath: pkgPath, |  | ||||||
| 						ID:      id, |  | ||||||
| 						Name:    pkgName, |  | ||||||
| 						Imports: make(map[string]*Package), |  | ||||||
| 					} |  | ||||||
| 					response.addPackage(pkg) |  | ||||||
| 					havePkgs[pkg.PkgPath] = id |  | ||||||
| 					// Add the production package's sources for a test variant. |  | ||||||
| 					if isTestFile && !isXTest && testVariantOf != nil { |  | ||||||
| 						pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) |  | ||||||
| 						pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) |  | ||||||
| 						// Add the package under test and its imports to the test variant. |  | ||||||
| 						pkg.forTest = testVariantOf.PkgPath |  | ||||||
| 						for k, v := range testVariantOf.Imports { |  | ||||||
| 							pkg.Imports[k] = &Package{ID: v.ID} |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					if isXTest { |  | ||||||
| 						pkg.forTest = forTest |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !fileExists { |  | ||||||
| 			pkg.GoFiles = append(pkg.GoFiles, opath) |  | ||||||
| 			// TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior |  | ||||||
| 			// if the file will be ignored due to its build tags. |  | ||||||
| 			pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath) |  | ||||||
| 			modifiedPkgsSet[pkg.ID] = true |  | ||||||
| 		} |  | ||||||
| 		imports, err := extractImports(opath, contents) |  | ||||||
| 		if err != nil { |  | ||||||
| 			// Let the parser or type checker report errors later. |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		for _, imp := range imports { |  | ||||||
| 			// TODO(rstambler): If the package is an x test and the import has |  | ||||||
| 			// a test variant, make sure to replace it. |  | ||||||
| 			if _, found := pkg.Imports[imp]; found { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			overlayAddsImports = true |  | ||||||
| 			id, ok := havePkgs[imp] |  | ||||||
| 			if !ok { |  | ||||||
| 				var err error |  | ||||||
| 				id, err = state.resolveImport(dir, imp) |  | ||||||
| 				if err != nil { |  | ||||||
| 					return nil, nil, err |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			pkg.Imports[imp] = &Package{ID: id} |  | ||||||
| 			// Add dependencies to the non-test variant version of this package as well. |  | ||||||
| 			if testVariantOf != nil { |  | ||||||
| 				testVariantOf.Imports[imp] = &Package{ID: id} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// toPkgPath guesses the package path given the id. |  | ||||||
| 	toPkgPath := func(sourceDir, id string) (string, error) { |  | ||||||
| 		if i := strings.IndexByte(id, ' '); i >= 0 { |  | ||||||
| 			return state.resolveImport(sourceDir, id[:i]) |  | ||||||
| 		} |  | ||||||
| 		return state.resolveImport(sourceDir, id) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Now that new packages have been created, do another pass to determine |  | ||||||
| 	// the new set of missing packages. |  | ||||||
| 	for _, pkg := range response.dr.Packages { |  | ||||||
| 		for _, imp := range pkg.Imports { |  | ||||||
| 			if len(pkg.GoFiles) == 0 { |  | ||||||
| 				return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath) |  | ||||||
| 			} |  | ||||||
| 			pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, nil, err |  | ||||||
| 			} |  | ||||||
| 			if _, ok := havePkgs[pkgPath]; !ok { |  | ||||||
| 				needPkgsSet[pkgPath] = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if overlayAddsImports { |  | ||||||
| 		needPkgs = make([]string, 0, len(needPkgsSet)) |  | ||||||
| 		for pkg := range needPkgsSet { |  | ||||||
| 			needPkgs = append(needPkgs, pkg) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) |  | ||||||
| 	for pkg := range modifiedPkgsSet { |  | ||||||
| 		modifiedPkgs = append(modifiedPkgs, pkg) |  | ||||||
| 	} |  | ||||||
| 	return modifiedPkgs, needPkgs, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // resolveImport finds the ID of a package given its import path. |  | ||||||
| // In particular, it will find the right vendored copy when in GOPATH mode. |  | ||||||
| func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) { |  | ||||||
| 	env, err := state.getEnv() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	if env["GOMOD"] != "" { |  | ||||||
| 		return importPath, nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	searchDir := sourceDir |  | ||||||
| 	for { |  | ||||||
| 		vendorDir := filepath.Join(searchDir, "vendor") |  | ||||||
| 		exists, ok := state.vendorDirs[vendorDir] |  | ||||||
| 		if !ok { |  | ||||||
| 			info, err := os.Stat(vendorDir) |  | ||||||
| 			exists = err == nil && info.IsDir() |  | ||||||
| 			state.vendorDirs[vendorDir] = exists |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if exists { |  | ||||||
| 			vendoredPath := filepath.Join(vendorDir, importPath) |  | ||||||
| 			if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() { |  | ||||||
| 				// We should probably check for .go files here, but shame on anyone who fools us. |  | ||||||
| 				path, ok, err := state.getPkgPath(vendoredPath) |  | ||||||
| 				if err != nil { |  | ||||||
| 					return "", err |  | ||||||
| 				} |  | ||||||
| 				if ok { |  | ||||||
| 					return path, nil |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// We know we've hit the top of the filesystem when we Dir / and get /, |  | ||||||
| 		// or C:\ and get C:\, etc. |  | ||||||
| 		next := filepath.Dir(searchDir) |  | ||||||
| 		if next == searchDir { |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		searchDir = next |  | ||||||
| 	} |  | ||||||
| 	return importPath, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func hasTestFiles(p *Package) bool { |  | ||||||
| 	for _, f := range p.GoFiles { |  | ||||||
| 		if strings.HasSuffix(f, "_test.go") { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // determineRootDirs returns a mapping from absolute directories that could |  | ||||||
| // contain code to their corresponding import path prefixes. |  | ||||||
| func (state *golistState) determineRootDirs() (map[string]string, error) { |  | ||||||
| 	env, err := state.getEnv() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	if env["GOMOD"] != "" { |  | ||||||
| 		state.rootsOnce.Do(func() { |  | ||||||
| 			state.rootDirs, state.rootDirsError = state.determineRootDirsModules() |  | ||||||
| 		}) |  | ||||||
| 	} else { |  | ||||||
| 		state.rootsOnce.Do(func() { |  | ||||||
| 			state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH() |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| 	return state.rootDirs, state.rootDirsError |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (state *golistState) determineRootDirsModules() (map[string]string, error) { |  | ||||||
| 	// List all of the modules--the first will be the directory for the main |  | ||||||
| 	// module. Any replaced modules will also need to be treated as roots. |  | ||||||
| 	// Editing files in the module cache isn't a great idea, so we don't |  | ||||||
| 	// plan to ever support that. |  | ||||||
| 	out, err := state.invokeGo("list", "-m", "-json", "all") |  | ||||||
| 	if err != nil { |  | ||||||
| 		// 'go list all' will fail if we're outside of a module and |  | ||||||
| 		// GO111MODULE=on. Try falling back without 'all'. |  | ||||||
| 		var innerErr error |  | ||||||
| 		out, innerErr = state.invokeGo("list", "-m", "-json") |  | ||||||
| 		if innerErr != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	roots := map[string]string{} |  | ||||||
| 	modules := map[string]string{} |  | ||||||
| 	var i int |  | ||||||
| 	for dec := json.NewDecoder(out); dec.More(); { |  | ||||||
| 		mod := new(gocommand.ModuleJSON) |  | ||||||
| 		if err := dec.Decode(mod); err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		if mod.Dir != "" && mod.Path != "" { |  | ||||||
| 			// This is a valid module; add it to the map. |  | ||||||
| 			absDir, err := filepath.Abs(mod.Dir) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, err |  | ||||||
| 			} |  | ||||||
| 			modules[absDir] = mod.Path |  | ||||||
| 			// The first result is the main module. |  | ||||||
| 			if i == 0 || mod.Replace != nil && mod.Replace.Path != "" { |  | ||||||
| 				roots[absDir] = mod.Path |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		i++ |  | ||||||
| 	} |  | ||||||
| 	return roots, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { |  | ||||||
| 	m := map[string]string{} |  | ||||||
| 	for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) { |  | ||||||
| 		absDir, err := filepath.Abs(dir) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		m[filepath.Join(absDir, "src")] = "" |  | ||||||
| 	} |  | ||||||
| 	return m, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func extractImports(filename string, contents []byte) ([]string, error) { |  | ||||||
| 	f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset? |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	var res []string |  | ||||||
| 	for _, imp := range f.Imports { |  | ||||||
| 		quotedPath := imp.Path.Value |  | ||||||
| 		path, err := strconv.Unquote(quotedPath) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		res = append(res, path) |  | ||||||
| 	} |  | ||||||
| 	return res, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // reclaimPackage attempts to reuse a package that failed to load in an overlay. |  | ||||||
| // |  | ||||||
| // If the package has errors and has no Name, GoFiles, or Imports, |  | ||||||
| // then it's possible that it doesn't yet exist on disk. |  | ||||||
| func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool { |  | ||||||
| 	// TODO(rstambler): Check the message of the actual error? |  | ||||||
| 	// It differs between $GOPATH and module mode. |  | ||||||
| 	if pkg.ID != id { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	if len(pkg.Errors) != 1 { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	if pkg.Name != "" || pkg.ExportFile != "" { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	if len(pkg.Imports) > 0 { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	pkgName, ok := extractPackageName(filename, contents) |  | ||||||
| 	if !ok { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	pkg.Name = pkgName |  | ||||||
| 	pkg.Errors = nil |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func extractPackageName(filename string, contents []byte) (string, bool) { |  | ||||||
| 	// TODO(rstambler): Check the message of the actual error? |  | ||||||
| 	// It differs between $GOPATH and module mode. |  | ||||||
| 	f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset? |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", false |  | ||||||
| 	} |  | ||||||
| 	return f.Name.Name, true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func commonDir(a []string) string { |  | ||||||
| 	seen := make(map[string]bool) |  | ||||||
| 	x := append([]string{}, a...) |  | ||||||
| 	for _, f := range x { |  | ||||||
| 		seen[filepath.Dir(f)] = true |  | ||||||
| 	} |  | ||||||
| 	if len(seen) > 1 { |  | ||||||
| 		log.Fatalf("commonDir saw %v for %v", seen, x) |  | ||||||
| 	} |  | ||||||
| 	for k := range seen { |  | ||||||
| 		// len(seen) == 1 |  | ||||||
| 		return k |  | ||||||
| 	} |  | ||||||
| 	return "" // no files |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // It is possible that the files in the disk directory dir have a different package |  | ||||||
| // name from newName, which is deduced from the overlays. If they all have a different |  | ||||||
| // package name, and they all have the same package name, then that name becomes |  | ||||||
| // the package name. |  | ||||||
| // It returns true if it changes the package name, false otherwise. |  | ||||||
| func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) { |  | ||||||
| 	names := make(map[string]int) |  | ||||||
| 	for _, p := range pkgsOfDir { |  | ||||||
| 		names[p.Name]++ |  | ||||||
| 	} |  | ||||||
| 	if len(names) != 1 { |  | ||||||
| 		// some files are in different packages |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	var oldName string |  | ||||||
| 	for k := range names { |  | ||||||
| 		oldName = k |  | ||||||
| 	} |  | ||||||
| 	if newName == oldName { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// We might have a case where all of the package names in the directory are |  | ||||||
| 	// the same, but the overlay file is for an x test, which belongs to its |  | ||||||
| 	// own package. If the x test does not yet exist on disk, we may not yet |  | ||||||
| 	// have its package name on disk, but we should not rename the packages. |  | ||||||
| 	// |  | ||||||
| 	// We use a heuristic to determine if this file belongs to an x test: |  | ||||||
| 	// The test file should have a package name whose package name has a _test |  | ||||||
| 	// suffix or looks like "newName_test". |  | ||||||
| 	maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test") |  | ||||||
| 	if isTestFile && maybeXTest { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	for _, p := range pkgsOfDir { |  | ||||||
| 		p.Name = newName |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // This function is copy-pasted from |  | ||||||
| // https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360. |  | ||||||
| // It should be deleted when we remove support for overlays from go/packages. |  | ||||||
| // |  | ||||||
| // NOTE: This does not handle any ./... or ./ style queries, as this function |  | ||||||
| // doesn't know the working directory. |  | ||||||
| // |  | ||||||
| // matchPattern(pattern)(name) reports whether |  | ||||||
| // name matches pattern. Pattern is a limited glob |  | ||||||
| // pattern in which '...' means 'any string' and there |  | ||||||
| // is no other special syntax. |  | ||||||
| // Unfortunately, there are two special cases. Quoting "go help packages": |  | ||||||
| // |  | ||||||
| // First, /... at the end of the pattern can match an empty string, |  | ||||||
| // so that net/... matches both net and packages in its subdirectories, like net/http. |  | ||||||
| // Second, any slash-separated pattern element containing a wildcard never |  | ||||||
| // participates in a match of the "vendor" element in the path of a vendored |  | ||||||
| // package, so that ./... does not match packages in subdirectories of |  | ||||||
| // ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. |  | ||||||
| // Note, however, that a directory named vendor that itself contains code |  | ||||||
| // is not a vendored package: cmd/vendor would be a command named vendor, |  | ||||||
| // and the pattern cmd/... matches it. |  | ||||||
| func matchPattern(pattern string) func(name string) bool { |  | ||||||
| 	// Convert pattern to regular expression. |  | ||||||
| 	// The strategy for the trailing /... is to nest it in an explicit ? expression. |  | ||||||
| 	// The strategy for the vendor exclusion is to change the unmatchable |  | ||||||
| 	// vendor strings to a disallowed code point (vendorChar) and to use |  | ||||||
| 	// "(anything but that codepoint)*" as the implementation of the ... wildcard. |  | ||||||
| 	// This is a bit complicated but the obvious alternative, |  | ||||||
| 	// namely a hand-written search like in most shell glob matchers, |  | ||||||
| 	// is too easy to make accidentally exponential. |  | ||||||
| 	// Using package regexp guarantees linear-time matching. |  | ||||||
| 
 |  | ||||||
| 	const vendorChar = "\x00" |  | ||||||
| 
 |  | ||||||
| 	if strings.Contains(pattern, vendorChar) { |  | ||||||
| 		return func(name string) bool { return false } |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	re := regexp.QuoteMeta(pattern) |  | ||||||
| 	re = replaceVendor(re, vendorChar) |  | ||||||
| 	switch { |  | ||||||
| 	case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): |  | ||||||
| 		re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` |  | ||||||
| 	case re == vendorChar+`/\.\.\.`: |  | ||||||
| 		re = `(/vendor|/` + vendorChar + `/\.\.\.)` |  | ||||||
| 	case strings.HasSuffix(re, `/\.\.\.`): |  | ||||||
| 		re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` |  | ||||||
| 	} |  | ||||||
| 	re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`) |  | ||||||
| 
 |  | ||||||
| 	reg := regexp.MustCompile(`^` + re + `$`) |  | ||||||
| 
 |  | ||||||
| 	return func(name string) bool { |  | ||||||
| 		if strings.Contains(name, vendorChar) { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 		return reg.MatchString(replaceVendor(name, vendorChar)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // replaceVendor returns the result of replacing |  | ||||||
| // non-trailing vendor path elements in x with repl. |  | ||||||
| func replaceVendor(x, repl string) string { |  | ||||||
| 	if !strings.Contains(x, "vendor") { |  | ||||||
| 		return x |  | ||||||
| 	} |  | ||||||
| 	elem := strings.Split(x, "/") |  | ||||||
| 	for i := 0; i < len(elem)-1; i++ { |  | ||||||
| 		if elem[i] == "vendor" { |  | ||||||
| 			elem[i] = repl |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return strings.Join(elem, "/") |  | ||||||
| } |  | ||||||
							
								
								
									
										1212
									
								
								vendor/golang.org/x/tools/go/packages/packages.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1212
									
								
								vendor/golang.org/x/tools/go/packages/packages.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										55
									
								
								vendor/golang.org/x/tools/go/packages/visit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										55
									
								
								vendor/golang.org/x/tools/go/packages/visit.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,55 +0,0 @@ | |||||||
| package packages |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"sort" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Visit visits all the packages in the import graph whose roots are |  | ||||||
| // pkgs, calling the optional pre function the first time each package |  | ||||||
| // is encountered (preorder), and the optional post function after a |  | ||||||
| // package's dependencies have been visited (postorder). |  | ||||||
| // The boolean result of pre(pkg) determines whether |  | ||||||
| // the imports of package pkg are visited. |  | ||||||
| func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { |  | ||||||
| 	seen := make(map[*Package]bool) |  | ||||||
| 	var visit func(*Package) |  | ||||||
| 	visit = func(pkg *Package) { |  | ||||||
| 		if !seen[pkg] { |  | ||||||
| 			seen[pkg] = true |  | ||||||
| 
 |  | ||||||
| 			if pre == nil || pre(pkg) { |  | ||||||
| 				paths := make([]string, 0, len(pkg.Imports)) |  | ||||||
| 				for path := range pkg.Imports { |  | ||||||
| 					paths = append(paths, path) |  | ||||||
| 				} |  | ||||||
| 				sort.Strings(paths) // Imports is a map, this makes visit stable |  | ||||||
| 				for _, path := range paths { |  | ||||||
| 					visit(pkg.Imports[path]) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if post != nil { |  | ||||||
| 				post(pkg) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	for _, pkg := range pkgs { |  | ||||||
| 		visit(pkg) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // PrintErrors prints to os.Stderr the accumulated errors of all |  | ||||||
| // packages in the import graph rooted at pkgs, dependencies first. |  | ||||||
| // PrintErrors returns the number of errors printed. |  | ||||||
| func PrintErrors(pkgs []*Package) int { |  | ||||||
| 	var n int |  | ||||||
| 	Visit(pkgs, nil, func(pkg *Package) { |  | ||||||
| 		for _, err := range pkg.Errors { |  | ||||||
| 			fmt.Fprintln(os.Stderr, err) |  | ||||||
| 			n++ |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| 	return n |  | ||||||
| } |  | ||||||
							
								
								
									
										85
									
								
								vendor/golang.org/x/tools/internal/event/core/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										85
									
								
								vendor/golang.org/x/tools/internal/event/core/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,85 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package core provides support for event based telemetry. |  | ||||||
| package core |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/event/label" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Event holds the information about an event of note that ocurred. |  | ||||||
| type Event struct { |  | ||||||
| 	at time.Time |  | ||||||
| 
 |  | ||||||
| 	// As events are often on the stack, storing the first few labels directly |  | ||||||
| 	// in the event can avoid an allocation at all for the very common cases of |  | ||||||
| 	// simple events. |  | ||||||
| 	// The length needs to be large enough to cope with the majority of events |  | ||||||
| 	// but no so large as to cause undue stack pressure. |  | ||||||
| 	// A log message with two values will use 3 labels (one for each value and |  | ||||||
| 	// one for the message itself). |  | ||||||
| 
 |  | ||||||
| 	static  [3]label.Label // inline storage for the first few labels |  | ||||||
| 	dynamic []label.Label  // dynamically sized storage for remaining labels |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // eventLabelMap implements label.Map for a the labels of an Event. |  | ||||||
| type eventLabelMap struct { |  | ||||||
| 	event Event |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ev Event) At() time.Time { return ev.at } |  | ||||||
| 
 |  | ||||||
| func (ev Event) Format(f fmt.State, r rune) { |  | ||||||
| 	if !ev.at.IsZero() { |  | ||||||
| 		fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 ")) |  | ||||||
| 	} |  | ||||||
| 	for index := 0; ev.Valid(index); index++ { |  | ||||||
| 		if l := ev.Label(index); l.Valid() { |  | ||||||
| 			fmt.Fprintf(f, "\n\t%v", l) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ev Event) Valid(index int) bool { |  | ||||||
| 	return index >= 0 && index < len(ev.static)+len(ev.dynamic) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ev Event) Label(index int) label.Label { |  | ||||||
| 	if index < len(ev.static) { |  | ||||||
| 		return ev.static[index] |  | ||||||
| 	} |  | ||||||
| 	return ev.dynamic[index-len(ev.static)] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (ev Event) Find(key label.Key) label.Label { |  | ||||||
| 	for _, l := range ev.static { |  | ||||||
| 		if l.Key() == key { |  | ||||||
| 			return l |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	for _, l := range ev.dynamic { |  | ||||||
| 		if l.Key() == key { |  | ||||||
| 			return l |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return label.Label{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func MakeEvent(static [3]label.Label, labels []label.Label) Event { |  | ||||||
| 	return Event{ |  | ||||||
| 		static:  static, |  | ||||||
| 		dynamic: labels, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // CloneEvent event returns a copy of the event with the time adjusted to at. |  | ||||||
| func CloneEvent(ev Event, at time.Time) Event { |  | ||||||
| 	ev.at = at |  | ||||||
| 	return ev |  | ||||||
| } |  | ||||||
							
								
								
									
										70
									
								
								vendor/golang.org/x/tools/internal/event/core/export.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								vendor/golang.org/x/tools/internal/event/core/export.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,70 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package core |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"sync/atomic" |  | ||||||
| 	"time" |  | ||||||
| 	"unsafe" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/event/label" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Exporter is a function that handles events. |  | ||||||
| // It may return a modified context and event. |  | ||||||
| type Exporter func(context.Context, Event, label.Map) context.Context |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	exporter unsafe.Pointer |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // SetExporter sets the global exporter function that handles all events. |  | ||||||
| // The exporter is called synchronously from the event call site, so it should |  | ||||||
| // return quickly so as not to hold up user code. |  | ||||||
| func SetExporter(e Exporter) { |  | ||||||
| 	p := unsafe.Pointer(&e) |  | ||||||
| 	if e == nil { |  | ||||||
| 		// &e is always valid, and so p is always valid, but for the early abort |  | ||||||
| 		// of ProcessEvent to be efficient it needs to make the nil check on the |  | ||||||
| 		// pointer without having to dereference it, so we make the nil function |  | ||||||
| 		// also a nil pointer |  | ||||||
| 		p = nil |  | ||||||
| 	} |  | ||||||
| 	atomic.StorePointer(&exporter, p) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // deliver is called to deliver an event to the supplied exporter. |  | ||||||
| // it will fill in the time. |  | ||||||
| func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context { |  | ||||||
| 	// add the current time to the event |  | ||||||
| 	ev.at = time.Now() |  | ||||||
| 	// hand the event off to the current exporter |  | ||||||
| 	return exporter(ctx, ev, ev) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Export is called to deliver an event to the global exporter if set. |  | ||||||
| func Export(ctx context.Context, ev Event) context.Context { |  | ||||||
| 	// get the global exporter and abort early if there is not one |  | ||||||
| 	exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) |  | ||||||
| 	if exporterPtr == nil { |  | ||||||
| 		return ctx |  | ||||||
| 	} |  | ||||||
| 	return deliver(ctx, *exporterPtr, ev) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ExportPair is called to deliver a start event to the supplied exporter. |  | ||||||
| // It also returns a function that will deliver the end event to the same |  | ||||||
| // exporter. |  | ||||||
| // It will fill in the time. |  | ||||||
| func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) { |  | ||||||
| 	// get the global exporter and abort early if there is not one |  | ||||||
| 	exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) |  | ||||||
| 	if exporterPtr == nil { |  | ||||||
| 		return ctx, func() {} |  | ||||||
| 	} |  | ||||||
| 	ctx = deliver(ctx, *exporterPtr, begin) |  | ||||||
| 	return ctx, func() { deliver(ctx, *exporterPtr, end) } |  | ||||||
| } |  | ||||||
							
								
								
									
										77
									
								
								vendor/golang.org/x/tools/internal/event/core/fast.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										77
									
								
								vendor/golang.org/x/tools/internal/event/core/fast.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,77 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package core |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/event/keys" |  | ||||||
| 	"golang.org/x/tools/internal/event/label" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Log1 takes a message and one label delivers a log event to the exporter. |  | ||||||
| // It is a customized version of Print that is faster and does no allocation. |  | ||||||
| func Log1(ctx context.Context, message string, t1 label.Label) { |  | ||||||
| 	Export(ctx, MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Msg.Of(message), |  | ||||||
| 		t1, |  | ||||||
| 	}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Log2 takes a message and two labels and delivers a log event to the exporter. |  | ||||||
| // It is a customized version of Print that is faster and does no allocation. |  | ||||||
| func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) { |  | ||||||
| 	Export(ctx, MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Msg.Of(message), |  | ||||||
| 		t1, |  | ||||||
| 		t2, |  | ||||||
| 	}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Metric1 sends a label event to the exporter with the supplied labels. |  | ||||||
| func Metric1(ctx context.Context, t1 label.Label) context.Context { |  | ||||||
| 	return Export(ctx, MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Metric.New(), |  | ||||||
| 		t1, |  | ||||||
| 	}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Metric2 sends a label event to the exporter with the supplied labels. |  | ||||||
| func Metric2(ctx context.Context, t1, t2 label.Label) context.Context { |  | ||||||
| 	return Export(ctx, MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Metric.New(), |  | ||||||
| 		t1, |  | ||||||
| 		t2, |  | ||||||
| 	}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Start1 sends a span start event with the supplied label list to the exporter. |  | ||||||
| // It also returns a function that will end the span, which should normally be |  | ||||||
| // deferred. |  | ||||||
| func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) { |  | ||||||
| 	return ExportPair(ctx, |  | ||||||
| 		MakeEvent([3]label.Label{ |  | ||||||
| 			keys.Start.Of(name), |  | ||||||
| 			t1, |  | ||||||
| 		}, nil), |  | ||||||
| 		MakeEvent([3]label.Label{ |  | ||||||
| 			keys.End.New(), |  | ||||||
| 		}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Start2 sends a span start event with the supplied label list to the exporter. |  | ||||||
| // It also returns a function that will end the span, which should normally be |  | ||||||
| // deferred. |  | ||||||
| func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) { |  | ||||||
| 	return ExportPair(ctx, |  | ||||||
| 		MakeEvent([3]label.Label{ |  | ||||||
| 			keys.Start.Of(name), |  | ||||||
| 			t1, |  | ||||||
| 			t2, |  | ||||||
| 		}, nil), |  | ||||||
| 		MakeEvent([3]label.Label{ |  | ||||||
| 			keys.End.New(), |  | ||||||
| 		}, nil)) |  | ||||||
| } |  | ||||||
							
								
								
									
										7
									
								
								vendor/golang.org/x/tools/internal/event/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/golang.org/x/tools/internal/event/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,7 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package event provides a set of packages that cover the main |  | ||||||
| // concepts of telemetry in an implementation agnostic way. |  | ||||||
| package event |  | ||||||
							
								
								
									
										127
									
								
								vendor/golang.org/x/tools/internal/event/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										127
									
								
								vendor/golang.org/x/tools/internal/event/event.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,127 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package event |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/event/core" |  | ||||||
| 	"golang.org/x/tools/internal/event/keys" |  | ||||||
| 	"golang.org/x/tools/internal/event/label" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Exporter is a function that handles events. |  | ||||||
| // It may return a modified context and event. |  | ||||||
| type Exporter func(context.Context, core.Event, label.Map) context.Context |  | ||||||
| 
 |  | ||||||
| // SetExporter sets the global exporter function that handles all events. |  | ||||||
| // The exporter is called synchronously from the event call site, so it should |  | ||||||
| // return quickly so as not to hold up user code. |  | ||||||
| func SetExporter(e Exporter) { |  | ||||||
| 	core.SetExporter(core.Exporter(e)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Log takes a message and a label list and combines them into a single event |  | ||||||
| // before delivering them to the exporter. |  | ||||||
| func Log(ctx context.Context, message string, labels ...label.Label) { |  | ||||||
| 	core.Export(ctx, core.MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Msg.Of(message), |  | ||||||
| 	}, labels)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsLog returns true if the event was built by the Log function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsLog(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.Msg |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Error takes a message and a label list and combines them into a single event |  | ||||||
| // before delivering them to the exporter. It captures the error in the |  | ||||||
| // delivered event. |  | ||||||
| func Error(ctx context.Context, message string, err error, labels ...label.Label) { |  | ||||||
| 	core.Export(ctx, core.MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Msg.Of(message), |  | ||||||
| 		keys.Err.Of(err), |  | ||||||
| 	}, labels)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsError returns true if the event was built by the Error function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsError(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.Msg && |  | ||||||
| 		ev.Label(1).Key() == keys.Err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Metric sends a label event to the exporter with the supplied labels. |  | ||||||
| func Metric(ctx context.Context, labels ...label.Label) { |  | ||||||
| 	core.Export(ctx, core.MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Metric.New(), |  | ||||||
| 	}, labels)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsMetric returns true if the event was built by the Metric function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsMetric(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.Metric |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Label sends a label event to the exporter with the supplied labels. |  | ||||||
| func Label(ctx context.Context, labels ...label.Label) context.Context { |  | ||||||
| 	return core.Export(ctx, core.MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Label.New(), |  | ||||||
| 	}, labels)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsLabel returns true if the event was built by the Label function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsLabel(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.Label |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Start sends a span start event with the supplied label list to the exporter. |  | ||||||
| // It also returns a function that will end the span, which should normally be |  | ||||||
| // deferred. |  | ||||||
| func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) { |  | ||||||
| 	return core.ExportPair(ctx, |  | ||||||
| 		core.MakeEvent([3]label.Label{ |  | ||||||
| 			keys.Start.Of(name), |  | ||||||
| 		}, labels), |  | ||||||
| 		core.MakeEvent([3]label.Label{ |  | ||||||
| 			keys.End.New(), |  | ||||||
| 		}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsStart returns true if the event was built by the Start function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsStart(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.Start |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsEnd returns true if the event was built by the End function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsEnd(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.End |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Detach returns a context without an associated span. |  | ||||||
| // This allows the creation of spans that are not children of the current span. |  | ||||||
| func Detach(ctx context.Context) context.Context { |  | ||||||
| 	return core.Export(ctx, core.MakeEvent([3]label.Label{ |  | ||||||
| 		keys.Detach.New(), |  | ||||||
| 	}, nil)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IsDetach returns true if the event was built by the Detach function. |  | ||||||
| // It is intended to be used in exporters to identify the semantics of the |  | ||||||
| // event when deciding what to do with it. |  | ||||||
| func IsDetach(ev core.Event) bool { |  | ||||||
| 	return ev.Label(0).Key() == keys.Detach |  | ||||||
| } |  | ||||||
							
								
								
									
										564
									
								
								vendor/golang.org/x/tools/internal/event/keys/keys.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										564
									
								
								vendor/golang.org/x/tools/internal/event/keys/keys.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,564 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package keys |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"math" |  | ||||||
| 	"strconv" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/event/label" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Value represents a key for untyped values. |  | ||||||
| type Value struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // New creates a new Key for untyped values. |  | ||||||
| func New(name, description string) *Value { |  | ||||||
| 	return &Value{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Value) Name() string        { return k.name } |  | ||||||
| func (k *Value) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Value) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	fmt.Fprint(w, k.From(l)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Value) Get(lm label.Map) interface{} { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) } |  | ||||||
| 
 |  | ||||||
| // Tag represents a key for tagging labels that have no value. |  | ||||||
| // These are used when the existence of the label is the entire information it |  | ||||||
| // carries, such as marking events to be of a specific kind, or from a specific |  | ||||||
| // package. |  | ||||||
| type Tag struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewTag creates a new Key for tagging labels. |  | ||||||
| func NewTag(name, description string) *Tag { |  | ||||||
| 	return &Tag{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Tag) Name() string        { return k.name } |  | ||||||
| func (k *Tag) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {} |  | ||||||
| 
 |  | ||||||
| // New creates a new Label with this key. |  | ||||||
| func (k *Tag) New() label.Label { return label.OfValue(k, nil) } |  | ||||||
| 
 |  | ||||||
| // Int represents a key |  | ||||||
| type Int struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewInt creates a new Key for int values. |  | ||||||
| func NewInt(name, description string) *Int { |  | ||||||
| 	return &Int{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Int) Name() string        { return k.name } |  | ||||||
| func (k *Int) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Int) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Int) Get(lm label.Map) int { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Int) From(t label.Label) int { return int(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // Int8 represents a key |  | ||||||
| type Int8 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewInt8 creates a new Key for int8 values. |  | ||||||
| func NewInt8(name, description string) *Int8 { |  | ||||||
| 	return &Int8{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Int8) Name() string        { return k.name } |  | ||||||
| func (k *Int8) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Int8) Get(lm label.Map) int8 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // Int16 represents a key |  | ||||||
| type Int16 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewInt16 creates a new Key for int16 values. |  | ||||||
| func NewInt16(name, description string) *Int16 { |  | ||||||
| 	return &Int16{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Int16) Name() string        { return k.name } |  | ||||||
| func (k *Int16) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Int16) Get(lm label.Map) int16 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // Int32 represents a key |  | ||||||
| type Int32 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewInt32 creates a new Key for int32 values. |  | ||||||
| func NewInt32(name, description string) *Int32 { |  | ||||||
| 	return &Int32{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Int32) Name() string        { return k.name } |  | ||||||
| func (k *Int32) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Int32) Get(lm label.Map) int32 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // Int64 represents a key |  | ||||||
| type Int64 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewInt64 creates a new Key for int64 values. |  | ||||||
| func NewInt64(name, description string) *Int64 { |  | ||||||
| 	return &Int64{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Int64) Name() string        { return k.name } |  | ||||||
| func (k *Int64) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendInt(buf, k.From(l), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Int64) Get(lm label.Map) int64 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // UInt represents a key |  | ||||||
| type UInt struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewUInt creates a new Key for uint values. |  | ||||||
| func NewUInt(name, description string) *UInt { |  | ||||||
| 	return &UInt{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *UInt) Name() string        { return k.name } |  | ||||||
| func (k *UInt) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *UInt) Get(lm label.Map) uint { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // UInt8 represents a key |  | ||||||
| type UInt8 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewUInt8 creates a new Key for uint8 values. |  | ||||||
| func NewUInt8(name, description string) *UInt8 { |  | ||||||
| 	return &UInt8{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *UInt8) Name() string        { return k.name } |  | ||||||
| func (k *UInt8) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *UInt8) Get(lm label.Map) uint8 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // UInt16 represents a key |  | ||||||
| type UInt16 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewUInt16 creates a new Key for uint16 values. |  | ||||||
| func NewUInt16(name, description string) *UInt16 { |  | ||||||
| 	return &UInt16{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *UInt16) Name() string        { return k.name } |  | ||||||
| func (k *UInt16) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *UInt16) Get(lm label.Map) uint16 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // UInt32 represents a key |  | ||||||
| type UInt32 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewUInt32 creates a new Key for uint32 values. |  | ||||||
| func NewUInt32(name, description string) *UInt32 { |  | ||||||
| 	return &UInt32{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *UInt32) Name() string        { return k.name } |  | ||||||
| func (k *UInt32) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *UInt32) Get(lm label.Map) uint32 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) } |  | ||||||
| 
 |  | ||||||
| // UInt64 represents a key |  | ||||||
| type UInt64 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewUInt64 creates a new Key for uint64 values. |  | ||||||
| func NewUInt64(name, description string) *UInt64 { |  | ||||||
| 	return &UInt64{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *UInt64) Name() string        { return k.name } |  | ||||||
| func (k *UInt64) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendUint(buf, k.From(l), 10)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *UInt64) Get(lm label.Map) uint64 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() } |  | ||||||
| 
 |  | ||||||
| // Float32 represents a key |  | ||||||
| type Float32 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewFloat32 creates a new Key for float32 values. |  | ||||||
| func NewFloat32(name, description string) *Float32 { |  | ||||||
| 	return &Float32{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Float32) Name() string        { return k.name } |  | ||||||
| func (k *Float32) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Float32) Of(v float32) label.Label { |  | ||||||
| 	return label.Of64(k, uint64(math.Float32bits(v))) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Float32) Get(lm label.Map) float32 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Float32) From(t label.Label) float32 { |  | ||||||
| 	return math.Float32frombits(uint32(t.Unpack64())) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Float64 represents a key |  | ||||||
| type Float64 struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewFloat64 creates a new Key for int64 values. |  | ||||||
| func NewFloat64(name, description string) *Float64 { |  | ||||||
| 	return &Float64{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Float64) Name() string        { return k.name } |  | ||||||
| func (k *Float64) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Float64) Of(v float64) label.Label { |  | ||||||
| 	return label.Of64(k, math.Float64bits(v)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Float64) Get(lm label.Map) float64 { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Float64) From(t label.Label) float64 { |  | ||||||
| 	return math.Float64frombits(t.Unpack64()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // String represents a key |  | ||||||
| type String struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewString creates a new Key for int64 values. |  | ||||||
| func NewString(name, description string) *String { |  | ||||||
| 	return &String{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *String) Name() string        { return k.name } |  | ||||||
| func (k *String) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *String) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendQuote(buf, k.From(l))) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *String) Of(v string) label.Label { return label.OfString(k, v) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *String) Get(lm label.Map) string { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *String) From(t label.Label) string { return t.UnpackString() } |  | ||||||
| 
 |  | ||||||
| // Boolean represents a key |  | ||||||
| type Boolean struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewBoolean creates a new Key for bool values. |  | ||||||
| func NewBoolean(name, description string) *Boolean { |  | ||||||
| 	return &Boolean{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Boolean) Name() string        { return k.name } |  | ||||||
| func (k *Boolean) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	w.Write(strconv.AppendBool(buf, k.From(l))) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Boolean) Of(v bool) label.Label { |  | ||||||
| 	if v { |  | ||||||
| 		return label.Of64(k, 1) |  | ||||||
| 	} |  | ||||||
| 	return label.Of64(k, 0) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Boolean) Get(lm label.Map) bool { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 } |  | ||||||
| 
 |  | ||||||
| // Error represents a key |  | ||||||
| type Error struct { |  | ||||||
| 	name        string |  | ||||||
| 	description string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // NewError creates a new Key for int64 values. |  | ||||||
| func NewError(name, description string) *Error { |  | ||||||
| 	return &Error{name: name, description: description} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (k *Error) Name() string        { return k.name } |  | ||||||
| func (k *Error) Description() string { return k.description } |  | ||||||
| 
 |  | ||||||
| func (k *Error) Format(w io.Writer, buf []byte, l label.Label) { |  | ||||||
| 	io.WriteString(w, k.From(l).Error()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Of creates a new Label with this key and the supplied value. |  | ||||||
| func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) } |  | ||||||
| 
 |  | ||||||
| // Get can be used to get a label for the key from a label.Map. |  | ||||||
| func (k *Error) Get(lm label.Map) error { |  | ||||||
| 	if t := lm.Find(k); t.Valid() { |  | ||||||
| 		return k.From(t) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // From can be used to get a value from a Label. |  | ||||||
| func (k *Error) From(t label.Label) error { |  | ||||||
| 	err, _ := t.UnpackValue().(error) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/tools/internal/event/keys/standard.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/tools/internal/event/keys/standard.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,22 +0,0 @@ | |||||||
| // Copyright 2020 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package keys |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	// Msg is a key used to add message strings to label lists. |  | ||||||
| 	Msg = NewString("message", "a readable message") |  | ||||||
| 	// Label is a key used to indicate an event adds labels to the context. |  | ||||||
| 	Label = NewTag("label", "a label context marker") |  | ||||||
| 	// Start is used for things like traces that have a name. |  | ||||||
| 	Start = NewString("start", "span start") |  | ||||||
| 	// Metric is a key used to indicate an event records metrics. |  | ||||||
| 	End = NewTag("end", "a span end marker") |  | ||||||
| 	// Metric is a key used to indicate an event records metrics. |  | ||||||
| 	Detach = NewTag("detach", "a span detach marker") |  | ||||||
| 	// Err is a key used to add error values to label lists. |  | ||||||
| 	Err = NewError("error", "an error that occurred") |  | ||||||
| 	// Metric is a key used to indicate an event records metrics. |  | ||||||
| 	Metric = NewTag("metric", "a metric event marker") |  | ||||||
| ) |  | ||||||
							
								
								
									
										213
									
								
								vendor/golang.org/x/tools/internal/event/label/label.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										213
									
								
								vendor/golang.org/x/tools/internal/event/label/label.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,213 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package label |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"reflect" |  | ||||||
| 	"unsafe" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Key is used as the identity of a Label. |  | ||||||
| // Keys are intended to be compared by pointer only, the name should be unique |  | ||||||
| // for communicating with external systems, but it is not required or enforced. |  | ||||||
| type Key interface { |  | ||||||
| 	// Name returns the key name. |  | ||||||
| 	Name() string |  | ||||||
| 	// Description returns a string that can be used to describe the value. |  | ||||||
| 	Description() string |  | ||||||
| 
 |  | ||||||
| 	// Format is used in formatting to append the value of the label to the |  | ||||||
| 	// supplied buffer. |  | ||||||
| 	// The formatter may use the supplied buf as a scratch area to avoid |  | ||||||
| 	// allocations. |  | ||||||
| 	Format(w io.Writer, buf []byte, l Label) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Label holds a key and value pair. |  | ||||||
| // It is normally used when passing around lists of labels. |  | ||||||
| type Label struct { |  | ||||||
| 	key     Key |  | ||||||
| 	packed  uint64 |  | ||||||
| 	untyped interface{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Map is the interface to a collection of Labels indexed by key. |  | ||||||
| type Map interface { |  | ||||||
| 	// Find returns the label that matches the supplied key. |  | ||||||
| 	Find(key Key) Label |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // List is the interface to something that provides an iterable |  | ||||||
| // list of labels. |  | ||||||
| // Iteration should start from 0 and continue until Valid returns false. |  | ||||||
| type List interface { |  | ||||||
| 	// Valid returns true if the index is within range for the list. |  | ||||||
| 	// It does not imply the label at that index will itself be valid. |  | ||||||
| 	Valid(index int) bool |  | ||||||
| 	// Label returns the label at the given index. |  | ||||||
| 	Label(index int) Label |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // list implements LabelList for a list of Labels. |  | ||||||
| type list struct { |  | ||||||
| 	labels []Label |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // filter wraps a LabelList filtering out specific labels. |  | ||||||
| type filter struct { |  | ||||||
| 	keys       []Key |  | ||||||
| 	underlying List |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // listMap implements LabelMap for a simple list of labels. |  | ||||||
| type listMap struct { |  | ||||||
| 	labels []Label |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // mapChain implements LabelMap for a list of underlying LabelMap. |  | ||||||
| type mapChain struct { |  | ||||||
| 	maps []Map |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // OfValue creates a new label from the key and value. |  | ||||||
| // This method is for implementing new key types, label creation should |  | ||||||
| // normally be done with the Of method of the key. |  | ||||||
| func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} } |  | ||||||
| 
 |  | ||||||
| // UnpackValue assumes the label was built using LabelOfValue and returns the value |  | ||||||
| // that was passed to that constructor. |  | ||||||
| // This method is for implementing new key types, for type safety normal |  | ||||||
| // access should be done with the From method of the key. |  | ||||||
| func (t Label) UnpackValue() interface{} { return t.untyped } |  | ||||||
| 
 |  | ||||||
| // Of64 creates a new label from a key and a uint64. This is often |  | ||||||
| // used for non uint64 values that can be packed into a uint64. |  | ||||||
| // This method is for implementing new key types, label creation should |  | ||||||
| // normally be done with the Of method of the key. |  | ||||||
| func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} } |  | ||||||
| 
 |  | ||||||
| // Unpack64 assumes the label was built using LabelOf64 and returns the value that |  | ||||||
| // was passed to that constructor. |  | ||||||
| // This method is for implementing new key types, for type safety normal |  | ||||||
| // access should be done with the From method of the key. |  | ||||||
| func (t Label) Unpack64() uint64 { return t.packed } |  | ||||||
| 
 |  | ||||||
| // OfString creates a new label from a key and a string. |  | ||||||
| // This method is for implementing new key types, label creation should |  | ||||||
| // normally be done with the Of method of the key. |  | ||||||
| func OfString(k Key, v string) Label { |  | ||||||
| 	hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) |  | ||||||
| 	return Label{ |  | ||||||
| 		key:     k, |  | ||||||
| 		packed:  uint64(hdr.Len), |  | ||||||
| 		untyped: unsafe.Pointer(hdr.Data), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // UnpackString assumes the label was built using LabelOfString and returns the |  | ||||||
| // value that was passed to that constructor. |  | ||||||
| // This method is for implementing new key types, for type safety normal |  | ||||||
| // access should be done with the From method of the key. |  | ||||||
| func (t Label) UnpackString() string { |  | ||||||
| 	var v string |  | ||||||
| 	hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) |  | ||||||
| 	hdr.Data = uintptr(t.untyped.(unsafe.Pointer)) |  | ||||||
| 	hdr.Len = int(t.packed) |  | ||||||
| 	return *(*string)(unsafe.Pointer(hdr)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Valid returns true if the Label is a valid one (it has a key). |  | ||||||
| func (t Label) Valid() bool { return t.key != nil } |  | ||||||
| 
 |  | ||||||
| // Key returns the key of this Label. |  | ||||||
| func (t Label) Key() Key { return t.key } |  | ||||||
| 
 |  | ||||||
| // Format is used for debug printing of labels. |  | ||||||
| func (t Label) Format(f fmt.State, r rune) { |  | ||||||
| 	if !t.Valid() { |  | ||||||
| 		io.WriteString(f, `nil`) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	io.WriteString(f, t.Key().Name()) |  | ||||||
| 	io.WriteString(f, "=") |  | ||||||
| 	var buf [128]byte |  | ||||||
| 	t.Key().Format(f, buf[:0], t) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (l *list) Valid(index int) bool { |  | ||||||
| 	return index >= 0 && index < len(l.labels) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (l *list) Label(index int) Label { |  | ||||||
| 	return l.labels[index] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *filter) Valid(index int) bool { |  | ||||||
| 	return f.underlying.Valid(index) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (f *filter) Label(index int) Label { |  | ||||||
| 	l := f.underlying.Label(index) |  | ||||||
| 	for _, f := range f.keys { |  | ||||||
| 		if l.Key() == f { |  | ||||||
| 			return Label{} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return l |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (lm listMap) Find(key Key) Label { |  | ||||||
| 	for _, l := range lm.labels { |  | ||||||
| 		if l.Key() == key { |  | ||||||
| 			return l |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return Label{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (c mapChain) Find(key Key) Label { |  | ||||||
| 	for _, src := range c.maps { |  | ||||||
| 		l := src.Find(key) |  | ||||||
| 		if l.Valid() { |  | ||||||
| 			return l |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return Label{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var emptyList = &list{} |  | ||||||
| 
 |  | ||||||
| func NewList(labels ...Label) List { |  | ||||||
| 	if len(labels) == 0 { |  | ||||||
| 		return emptyList |  | ||||||
| 	} |  | ||||||
| 	return &list{labels: labels} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func Filter(l List, keys ...Key) List { |  | ||||||
| 	if len(keys) == 0 { |  | ||||||
| 		return l |  | ||||||
| 	} |  | ||||||
| 	return &filter{keys: keys, underlying: l} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NewMap(labels ...Label) Map { |  | ||||||
| 	return listMap{labels: labels} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func MergeMaps(srcs ...Map) Map { |  | ||||||
| 	var nonNil []Map |  | ||||||
| 	for _, src := range srcs { |  | ||||||
| 		if src != nil { |  | ||||||
| 			nonNil = append(nonNil, src) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if len(nonNil) == 1 { |  | ||||||
| 		return nonNil[0] |  | ||||||
| 	} |  | ||||||
| 	return mapChain{maps: nonNil} |  | ||||||
| } |  | ||||||
							
								
								
									
										230
									
								
								vendor/golang.org/x/tools/internal/gocommand/invoke.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										230
									
								
								vendor/golang.org/x/tools/internal/gocommand/invoke.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,230 +0,0 @@ | |||||||
| // Copyright 2020 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package gocommand is a helper for calling the go command. |  | ||||||
| package gocommand |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/tools/internal/event" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // An Runner will run go command invocations and serialize |  | ||||||
| // them if it sees a concurrency error. |  | ||||||
| type Runner struct { |  | ||||||
| 	// once guards the runner initialization. |  | ||||||
| 	once sync.Once |  | ||||||
| 
 |  | ||||||
| 	// inFlight tracks available workers. |  | ||||||
| 	inFlight chan struct{} |  | ||||||
| 
 |  | ||||||
| 	// serialized guards the ability to run a go command serially, |  | ||||||
| 	// to avoid deadlocks when claiming workers. |  | ||||||
| 	serialized chan struct{} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const maxInFlight = 10 |  | ||||||
| 
 |  | ||||||
| func (runner *Runner) initialize() { |  | ||||||
| 	runner.once.Do(func() { |  | ||||||
| 		runner.inFlight = make(chan struct{}, maxInFlight) |  | ||||||
| 		runner.serialized = make(chan struct{}, 1) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 1.13: go: updates to go.mod needed, but contents have changed |  | ||||||
| // 1.14: go: updating go.mod: existing contents have changed since last read |  | ||||||
| var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) |  | ||||||
| 
 |  | ||||||
| // Run is a convenience wrapper around RunRaw. |  | ||||||
| // It returns only stdout and a "friendly" error. |  | ||||||
| func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) { |  | ||||||
| 	stdout, _, friendly, _ := runner.RunRaw(ctx, inv) |  | ||||||
| 	return stdout, friendly |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RunPiped runs the invocation serially, always waiting for any concurrent |  | ||||||
| // invocations to complete first. |  | ||||||
| func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error { |  | ||||||
| 	_, err := runner.runPiped(ctx, inv, stdout, stderr) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RunRaw runs the invocation, serializing requests only if they fight over |  | ||||||
| // go.mod changes. |  | ||||||
| func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { |  | ||||||
| 	// Make sure the runner is always initialized. |  | ||||||
| 	runner.initialize() |  | ||||||
| 
 |  | ||||||
| 	// First, try to run the go command concurrently. |  | ||||||
| 	stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) |  | ||||||
| 
 |  | ||||||
| 	// If we encounter a load concurrency error, we need to retry serially. |  | ||||||
| 	if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { |  | ||||||
| 		return stdout, stderr, friendlyErr, err |  | ||||||
| 	} |  | ||||||
| 	event.Error(ctx, "Load concurrency error, will retry serially", err) |  | ||||||
| 
 |  | ||||||
| 	// Run serially by calling runPiped. |  | ||||||
| 	stdout.Reset() |  | ||||||
| 	stderr.Reset() |  | ||||||
| 	friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) |  | ||||||
| 	return stdout, stderr, friendlyErr, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { |  | ||||||
| 	// Wait for 1 worker to become available. |  | ||||||
| 	select { |  | ||||||
| 	case <-ctx.Done(): |  | ||||||
| 		return nil, nil, nil, ctx.Err() |  | ||||||
| 	case runner.inFlight <- struct{}{}: |  | ||||||
| 		defer func() { <-runner.inFlight }() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} |  | ||||||
| 	friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr) |  | ||||||
| 	return stdout, stderr, friendlyErr, err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { |  | ||||||
| 	// Make sure the runner is always initialized. |  | ||||||
| 	runner.initialize() |  | ||||||
| 
 |  | ||||||
| 	// Acquire the serialization lock. This avoids deadlocks between two |  | ||||||
| 	// runPiped commands. |  | ||||||
| 	select { |  | ||||||
| 	case <-ctx.Done(): |  | ||||||
| 		return nil, ctx.Err() |  | ||||||
| 	case runner.serialized <- struct{}{}: |  | ||||||
| 		defer func() { <-runner.serialized }() |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Wait for all in-progress go commands to return before proceeding, |  | ||||||
| 	// to avoid load concurrency errors. |  | ||||||
| 	for i := 0; i < maxInFlight; i++ { |  | ||||||
| 		select { |  | ||||||
| 		case <-ctx.Done(): |  | ||||||
| 			return nil, ctx.Err() |  | ||||||
| 		case runner.inFlight <- struct{}{}: |  | ||||||
| 			// Make sure we always "return" any workers we took. |  | ||||||
| 			defer func() { <-runner.inFlight }() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return inv.runWithFriendlyError(ctx, stdout, stderr) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // An Invocation represents a call to the go command. |  | ||||||
| type Invocation struct { |  | ||||||
| 	Verb       string |  | ||||||
| 	Args       []string |  | ||||||
| 	BuildFlags []string |  | ||||||
| 	Env        []string |  | ||||||
| 	WorkingDir string |  | ||||||
| 	Logf       func(format string, args ...interface{}) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { |  | ||||||
| 	rawError = i.run(ctx, stdout, stderr) |  | ||||||
| 	if rawError != nil { |  | ||||||
| 		friendlyError = rawError |  | ||||||
| 		// Check for 'go' executable not being found. |  | ||||||
| 		if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound { |  | ||||||
| 			friendlyError = fmt.Errorf("go command required, not found: %v", ee) |  | ||||||
| 		} |  | ||||||
| 		if ctx.Err() != nil { |  | ||||||
| 			friendlyError = ctx.Err() |  | ||||||
| 		} |  | ||||||
| 		friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { |  | ||||||
| 	log := i.Logf |  | ||||||
| 	if log == nil { |  | ||||||
| 		log = func(string, ...interface{}) {} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	goArgs := []string{i.Verb} |  | ||||||
| 	switch i.Verb { |  | ||||||
| 	case "mod": |  | ||||||
| 		// mod needs the sub-verb before build flags. |  | ||||||
| 		goArgs = append(goArgs, i.Args[0]) |  | ||||||
| 		goArgs = append(goArgs, i.BuildFlags...) |  | ||||||
| 		goArgs = append(goArgs, i.Args[1:]...) |  | ||||||
| 	case "env": |  | ||||||
| 		// env doesn't take build flags. |  | ||||||
| 		goArgs = append(goArgs, i.Args...) |  | ||||||
| 	default: |  | ||||||
| 		goArgs = append(goArgs, i.BuildFlags...) |  | ||||||
| 		goArgs = append(goArgs, i.Args...) |  | ||||||
| 	} |  | ||||||
| 	cmd := exec.Command("go", goArgs...) |  | ||||||
| 	cmd.Stdout = stdout |  | ||||||
| 	cmd.Stderr = stderr |  | ||||||
| 	// On darwin the cwd gets resolved to the real path, which breaks anything that |  | ||||||
| 	// expects the working directory to keep the original path, including the |  | ||||||
| 	// go command when dealing with modules. |  | ||||||
| 	// The Go stdlib has a special feature where if the cwd and the PWD are the |  | ||||||
| 	// same node then it trusts the PWD, so by setting it in the env for the child |  | ||||||
| 	// process we fix up all the paths returned by the go command. |  | ||||||
| 	cmd.Env = append(os.Environ(), i.Env...) |  | ||||||
| 	if i.WorkingDir != "" { |  | ||||||
| 		cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir) |  | ||||||
| 		cmd.Dir = i.WorkingDir |  | ||||||
| 	} |  | ||||||
| 	defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) |  | ||||||
| 
 |  | ||||||
| 	return runCmdContext(ctx, cmd) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // runCmdContext is like exec.CommandContext except it sends os.Interrupt |  | ||||||
| // before os.Kill. |  | ||||||
| func runCmdContext(ctx context.Context, cmd *exec.Cmd) error { |  | ||||||
| 	if err := cmd.Start(); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	resChan := make(chan error, 1) |  | ||||||
| 	go func() { |  | ||||||
| 		resChan <- cmd.Wait() |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	select { |  | ||||||
| 	case err := <-resChan: |  | ||||||
| 		return err |  | ||||||
| 	case <-ctx.Done(): |  | ||||||
| 	} |  | ||||||
| 	// Cancelled. Interrupt and see if it ends voluntarily. |  | ||||||
| 	cmd.Process.Signal(os.Interrupt) |  | ||||||
| 	select { |  | ||||||
| 	case err := <-resChan: |  | ||||||
| 		return err |  | ||||||
| 	case <-time.After(time.Second): |  | ||||||
| 	} |  | ||||||
| 	// Didn't shut down in response to interrupt. Kill it hard. |  | ||||||
| 	cmd.Process.Kill() |  | ||||||
| 	return <-resChan |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func cmdDebugStr(cmd *exec.Cmd) string { |  | ||||||
| 	env := make(map[string]string) |  | ||||||
| 	for _, kv := range cmd.Env { |  | ||||||
| 		split := strings.Split(kv, "=") |  | ||||||
| 		k, v := split[0], split[1] |  | ||||||
| 		env[k] = v |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args) |  | ||||||
| } |  | ||||||
							
								
								
									
										102
									
								
								vendor/golang.org/x/tools/internal/gocommand/vendor.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										102
									
								
								vendor/golang.org/x/tools/internal/gocommand/vendor.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,102 +0,0 @@ | |||||||
| // Copyright 2020 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package gocommand |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/mod/semver" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // ModuleJSON holds information about a module. |  | ||||||
| type ModuleJSON struct { |  | ||||||
| 	Path      string      // module path |  | ||||||
| 	Replace   *ModuleJSON // replaced by this module |  | ||||||
| 	Main      bool        // is this the main module? |  | ||||||
| 	Indirect  bool        // is this module only an indirect dependency of main module? |  | ||||||
| 	Dir       string      // directory holding files for this module, if any |  | ||||||
| 	GoMod     string      // path to go.mod file for this module, if any |  | ||||||
| 	GoVersion string      // go version used in module |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`) |  | ||||||
| 
 |  | ||||||
| // VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands |  | ||||||
| // with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields, |  | ||||||
| // of which only Verb and Args are modified to run the appropriate Go command. |  | ||||||
| // Inspired by setDefaultBuildMod in modload/init.go |  | ||||||
| func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { |  | ||||||
| 	mainMod, go114, err := getMainModuleAnd114(ctx, inv, r) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// We check the GOFLAGS to see if there is anything overridden or not. |  | ||||||
| 	inv.Verb = "env" |  | ||||||
| 	inv.Args = []string{"GOFLAGS"} |  | ||||||
| 	stdout, err := r.Run(ctx, inv) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, err |  | ||||||
| 	} |  | ||||||
| 	goflags := string(bytes.TrimSpace(stdout.Bytes())) |  | ||||||
| 	matches := modFlagRegexp.FindStringSubmatch(goflags) |  | ||||||
| 	var modFlag string |  | ||||||
| 	if len(matches) != 0 { |  | ||||||
| 		modFlag = matches[1] |  | ||||||
| 	} |  | ||||||
| 	if modFlag != "" { |  | ||||||
| 		// Don't override an explicit '-mod=' argument. |  | ||||||
| 		return mainMod, modFlag == "vendor", nil |  | ||||||
| 	} |  | ||||||
| 	if mainMod == nil || !go114 { |  | ||||||
| 		return mainMod, false, nil |  | ||||||
| 	} |  | ||||||
| 	// Check 1.14's automatic vendor mode. |  | ||||||
| 	if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() { |  | ||||||
| 		if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 { |  | ||||||
| 			// The Go version is at least 1.14, and a vendor directory exists. |  | ||||||
| 			// Set -mod=vendor by default. |  | ||||||
| 			return mainMod, true, nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return mainMod, false, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // getMainModuleAnd114 gets the main module's information and whether the |  | ||||||
| // go command in use is 1.14+. This is the information needed to figure out |  | ||||||
| // if vendoring should be enabled. |  | ||||||
| func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { |  | ||||||
| 	const format = `{{.Path}} |  | ||||||
| {{.Dir}} |  | ||||||
| {{.GoMod}} |  | ||||||
| {{.GoVersion}} |  | ||||||
| {{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} |  | ||||||
| ` |  | ||||||
| 	inv.Verb = "list" |  | ||||||
| 	inv.Args = []string{"-m", "-f", format} |  | ||||||
| 	stdout, err := r.Run(ctx, inv) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, false, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	lines := strings.Split(stdout.String(), "\n") |  | ||||||
| 	if len(lines) < 5 { |  | ||||||
| 		return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String()) |  | ||||||
| 	} |  | ||||||
| 	mod := &ModuleJSON{ |  | ||||||
| 		Path:      lines[0], |  | ||||||
| 		Dir:       lines[1], |  | ||||||
| 		GoMod:     lines[2], |  | ||||||
| 		GoVersion: lines[3], |  | ||||||
| 		Main:      true, |  | ||||||
| 	} |  | ||||||
| 	return mod, lines[4] == "go1.14", nil |  | ||||||
| } |  | ||||||
							
								
								
									
										14
									
								
								vendor/golang.org/x/tools/internal/packagesinternal/packages.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/golang.org/x/tools/internal/packagesinternal/packages.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,14 +0,0 @@ | |||||||
| // Package packagesinternal exposes internal-only fields from go/packages. |  | ||||||
| package packagesinternal |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"golang.org/x/tools/internal/gocommand" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var GetForTest = func(p interface{}) string { return "" } |  | ||||||
| 
 |  | ||||||
| var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } |  | ||||||
| 
 |  | ||||||
| var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} |  | ||||||
| 
 |  | ||||||
| var TypecheckCgo int |  | ||||||
							
								
								
									
										28
									
								
								vendor/golang.org/x/tools/internal/typesinternal/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/golang.org/x/tools/internal/typesinternal/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,28 +0,0 @@ | |||||||
| // Copyright 2020 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package typesinternal |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"go/types" |  | ||||||
| 	"reflect" |  | ||||||
| 	"unsafe" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func SetUsesCgo(conf *types.Config) bool { |  | ||||||
| 	v := reflect.ValueOf(conf).Elem() |  | ||||||
| 
 |  | ||||||
| 	f := v.FieldByName("go115UsesCgo") |  | ||||||
| 	if !f.IsValid() { |  | ||||||
| 		f = v.FieldByName("UsesCgo") |  | ||||||
| 		if !f.IsValid() { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	addr := unsafe.Pointer(f.UnsafeAddr()) |  | ||||||
| 	*(*bool)(addr) = true |  | ||||||
| 
 |  | ||||||
| 	return true |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/xerrors/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/xerrors/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,27 +0,0 @@ | |||||||
| Copyright (c) 2019 The Go Authors. All rights reserved. |  | ||||||
| 
 |  | ||||||
| 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. |  | ||||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/xerrors/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/xerrors/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,22 +0,0 @@ | |||||||
| Additional IP Rights Grant (Patents) |  | ||||||
| 
 |  | ||||||
| "This implementation" means the copyrightable works distributed by |  | ||||||
| Google as part of the Go project. |  | ||||||
| 
 |  | ||||||
| Google hereby grants to You a perpetual, worldwide, non-exclusive, |  | ||||||
| no-charge, royalty-free, irrevocable (except as stated in this section) |  | ||||||
| patent license to make, have made, use, offer to sell, sell, import, |  | ||||||
| transfer and otherwise run, modify and propagate the contents of this |  | ||||||
| implementation of Go, where such license applies only to those patent |  | ||||||
| claims, both currently owned or controlled by Google and acquired in |  | ||||||
| the future, licensable by Google that are necessarily infringed by this |  | ||||||
| implementation of Go.  This grant does not include claims that would be |  | ||||||
| infringed only as a consequence of further modification of this |  | ||||||
| implementation.  If you or your agent or exclusive licensee institute or |  | ||||||
| order or agree to the institution of patent litigation against any |  | ||||||
| entity (including a cross-claim or counterclaim in a lawsuit) alleging |  | ||||||
| that this implementation of Go or any code incorporated within this |  | ||||||
| implementation of Go constitutes direct or contributory patent |  | ||||||
| infringement, or inducement of patent infringement, then any patent |  | ||||||
| rights granted to you under this License for this implementation of Go |  | ||||||
| shall terminate as of the date such litigation is filed. |  | ||||||
							
								
								
									
										2
									
								
								vendor/golang.org/x/xerrors/README
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/golang.org/x/xerrors/README
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,2 +0,0 @@ | |||||||
| This repository holds the transition packages for the new Go 1.13 error values. |  | ||||||
| See golang.org/design/29934-error-values. |  | ||||||
							
								
								
									
										193
									
								
								vendor/golang.org/x/xerrors/adaptor.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										193
									
								
								vendor/golang.org/x/xerrors/adaptor.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,193 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package xerrors |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // FormatError calls the FormatError method of f with an errors.Printer |  | ||||||
| // configured according to s and verb, and writes the result to s. |  | ||||||
| func FormatError(f Formatter, s fmt.State, verb rune) { |  | ||||||
| 	// Assuming this function is only called from the Format method, and given |  | ||||||
| 	// that FormatError takes precedence over Format, it cannot be called from |  | ||||||
| 	// any package that supports errors.Formatter. It is therefore safe to |  | ||||||
| 	// disregard that State may be a specific printer implementation and use one |  | ||||||
| 	// of our choice instead. |  | ||||||
| 
 |  | ||||||
| 	// limitations: does not support printing error as Go struct. |  | ||||||
| 
 |  | ||||||
| 	var ( |  | ||||||
| 		sep    = " " // separator before next error |  | ||||||
| 		p      = &state{State: s} |  | ||||||
| 		direct = true |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	var err error = f |  | ||||||
| 
 |  | ||||||
| 	switch verb { |  | ||||||
| 	// Note that this switch must match the preference order |  | ||||||
| 	// for ordinary string printing (%#v before %+v, and so on). |  | ||||||
| 
 |  | ||||||
| 	case 'v': |  | ||||||
| 		if s.Flag('#') { |  | ||||||
| 			if stringer, ok := err.(fmt.GoStringer); ok { |  | ||||||
| 				io.WriteString(&p.buf, stringer.GoString()) |  | ||||||
| 				goto exit |  | ||||||
| 			} |  | ||||||
| 			// proceed as if it were %v |  | ||||||
| 		} else if s.Flag('+') { |  | ||||||
| 			p.printDetail = true |  | ||||||
| 			sep = "\n  - " |  | ||||||
| 		} |  | ||||||
| 	case 's': |  | ||||||
| 	case 'q', 'x', 'X': |  | ||||||
| 		// Use an intermediate buffer in the rare cases that precision, |  | ||||||
| 		// truncation, or one of the alternative verbs (q, x, and X) are |  | ||||||
| 		// specified. |  | ||||||
| 		direct = false |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		p.buf.WriteString("%!") |  | ||||||
| 		p.buf.WriteRune(verb) |  | ||||||
| 		p.buf.WriteByte('(') |  | ||||||
| 		switch { |  | ||||||
| 		case err != nil: |  | ||||||
| 			p.buf.WriteString(reflect.TypeOf(f).String()) |  | ||||||
| 		default: |  | ||||||
| 			p.buf.WriteString("<nil>") |  | ||||||
| 		} |  | ||||||
| 		p.buf.WriteByte(')') |  | ||||||
| 		io.Copy(s, &p.buf) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| loop: |  | ||||||
| 	for { |  | ||||||
| 		switch v := err.(type) { |  | ||||||
| 		case Formatter: |  | ||||||
| 			err = v.FormatError((*printer)(p)) |  | ||||||
| 		case fmt.Formatter: |  | ||||||
| 			v.Format(p, 'v') |  | ||||||
| 			break loop |  | ||||||
| 		default: |  | ||||||
| 			io.WriteString(&p.buf, v.Error()) |  | ||||||
| 			break loop |  | ||||||
| 		} |  | ||||||
| 		if err == nil { |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 		if p.needColon || !p.printDetail { |  | ||||||
| 			p.buf.WriteByte(':') |  | ||||||
| 			p.needColon = false |  | ||||||
| 		} |  | ||||||
| 		p.buf.WriteString(sep) |  | ||||||
| 		p.inDetail = false |  | ||||||
| 		p.needNewline = false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| exit: |  | ||||||
| 	width, okW := s.Width() |  | ||||||
| 	prec, okP := s.Precision() |  | ||||||
| 
 |  | ||||||
| 	if !direct || (okW && width > 0) || okP { |  | ||||||
| 		// Construct format string from State s. |  | ||||||
| 		format := []byte{'%'} |  | ||||||
| 		if s.Flag('-') { |  | ||||||
| 			format = append(format, '-') |  | ||||||
| 		} |  | ||||||
| 		if s.Flag('+') { |  | ||||||
| 			format = append(format, '+') |  | ||||||
| 		} |  | ||||||
| 		if s.Flag(' ') { |  | ||||||
| 			format = append(format, ' ') |  | ||||||
| 		} |  | ||||||
| 		if okW { |  | ||||||
| 			format = strconv.AppendInt(format, int64(width), 10) |  | ||||||
| 		} |  | ||||||
| 		if okP { |  | ||||||
| 			format = append(format, '.') |  | ||||||
| 			format = strconv.AppendInt(format, int64(prec), 10) |  | ||||||
| 		} |  | ||||||
| 		format = append(format, string(verb)...) |  | ||||||
| 		fmt.Fprintf(s, string(format), p.buf.String()) |  | ||||||
| 	} else { |  | ||||||
| 		io.Copy(s, &p.buf) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var detailSep = []byte("\n    ") |  | ||||||
| 
 |  | ||||||
| // state tracks error printing state. It implements fmt.State. |  | ||||||
| type state struct { |  | ||||||
| 	fmt.State |  | ||||||
| 	buf bytes.Buffer |  | ||||||
| 
 |  | ||||||
| 	printDetail bool |  | ||||||
| 	inDetail    bool |  | ||||||
| 	needColon   bool |  | ||||||
| 	needNewline bool |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (s *state) Write(b []byte) (n int, err error) { |  | ||||||
| 	if s.printDetail { |  | ||||||
| 		if len(b) == 0 { |  | ||||||
| 			return 0, nil |  | ||||||
| 		} |  | ||||||
| 		if s.inDetail && s.needColon { |  | ||||||
| 			s.needNewline = true |  | ||||||
| 			if b[0] == '\n' { |  | ||||||
| 				b = b[1:] |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		k := 0 |  | ||||||
| 		for i, c := range b { |  | ||||||
| 			if s.needNewline { |  | ||||||
| 				if s.inDetail && s.needColon { |  | ||||||
| 					s.buf.WriteByte(':') |  | ||||||
| 					s.needColon = false |  | ||||||
| 				} |  | ||||||
| 				s.buf.Write(detailSep) |  | ||||||
| 				s.needNewline = false |  | ||||||
| 			} |  | ||||||
| 			if c == '\n' { |  | ||||||
| 				s.buf.Write(b[k:i]) |  | ||||||
| 				k = i + 1 |  | ||||||
| 				s.needNewline = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		s.buf.Write(b[k:]) |  | ||||||
| 		if !s.inDetail { |  | ||||||
| 			s.needColon = true |  | ||||||
| 		} |  | ||||||
| 	} else if !s.inDetail { |  | ||||||
| 		s.buf.Write(b) |  | ||||||
| 	} |  | ||||||
| 	return len(b), nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // printer wraps a state to implement an xerrors.Printer. |  | ||||||
| type printer state |  | ||||||
| 
 |  | ||||||
| func (s *printer) Print(args ...interface{}) { |  | ||||||
| 	if !s.inDetail || s.printDetail { |  | ||||||
| 		fmt.Fprint((*state)(s), args...) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (s *printer) Printf(format string, args ...interface{}) { |  | ||||||
| 	if !s.inDetail || s.printDetail { |  | ||||||
| 		fmt.Fprintf((*state)(s), format, args...) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (s *printer) Detail() bool { |  | ||||||
| 	s.inDetail = true |  | ||||||
| 	return s.printDetail |  | ||||||
| } |  | ||||||
							
								
								
									
										1
									
								
								vendor/golang.org/x/xerrors/codereview.cfg
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/golang.org/x/xerrors/codereview.cfg
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | |||||||
| issuerepo: golang/go |  | ||||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/xerrors/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/xerrors/doc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,22 +0,0 @@ | |||||||
| // Copyright 2019 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| // Package xerrors implements functions to manipulate errors. |  | ||||||
| // |  | ||||||
| // This package is based on the Go 2 proposal for error values: |  | ||||||
| //   https://golang.org/design/29934-error-values |  | ||||||
| // |  | ||||||
| // These functions were incorporated into the standard library's errors package |  | ||||||
| // in Go 1.13: |  | ||||||
| // - Is |  | ||||||
| // - As |  | ||||||
| // - Unwrap |  | ||||||
| // |  | ||||||
| // Also, Errorf's %w verb was incorporated into fmt.Errorf. |  | ||||||
| // |  | ||||||
| // Use this package to get equivalent behavior in all supported Go versions. |  | ||||||
| // |  | ||||||
| // No other features of this package were included in Go 1.13, and at present |  | ||||||
| // there are no plans to include any of them. |  | ||||||
| package xerrors // import "golang.org/x/xerrors" |  | ||||||
							
								
								
									
										33
									
								
								vendor/golang.org/x/xerrors/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								vendor/golang.org/x/xerrors/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,33 +0,0 @@ | |||||||
| // Copyright 2011 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package xerrors |  | ||||||
| 
 |  | ||||||
| import "fmt" |  | ||||||
| 
 |  | ||||||
| // errorString is a trivial implementation of error. |  | ||||||
| type errorString struct { |  | ||||||
| 	s     string |  | ||||||
| 	frame Frame |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // New returns an error that formats as the given text. |  | ||||||
| // |  | ||||||
| // The returned error contains a Frame set to the caller's location and |  | ||||||
| // implements Formatter to show this information when printed with details. |  | ||||||
| func New(text string) error { |  | ||||||
| 	return &errorString{text, Caller(1)} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *errorString) Error() string { |  | ||||||
| 	return e.s |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } |  | ||||||
| 
 |  | ||||||
| func (e *errorString) FormatError(p Printer) (next error) { |  | ||||||
| 	p.Print(e.s) |  | ||||||
| 	e.frame.Format(p) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
							
								
								
									
										187
									
								
								vendor/golang.org/x/xerrors/fmt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										187
									
								
								vendor/golang.org/x/xerrors/fmt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,187 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package xerrors |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
| 	"unicode" |  | ||||||
| 	"unicode/utf8" |  | ||||||
| 
 |  | ||||||
| 	"golang.org/x/xerrors/internal" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const percentBangString = "%!" |  | ||||||
| 
 |  | ||||||
| // Errorf formats according to a format specifier and returns the string as a |  | ||||||
| // value that satisfies error. |  | ||||||
| // |  | ||||||
| // The returned error includes the file and line number of the caller when |  | ||||||
| // formatted with additional detail enabled. If the last argument is an error |  | ||||||
| // the returned error's Format method will return it if the format string ends |  | ||||||
| // with ": %s", ": %v", or ": %w". If the last argument is an error and the |  | ||||||
| // format string ends with ": %w", the returned error implements an Unwrap |  | ||||||
| // method returning it. |  | ||||||
| // |  | ||||||
| // If the format specifier includes a %w verb with an error operand in a |  | ||||||
| // position other than at the end, the returned error will still implement an |  | ||||||
| // Unwrap method returning the operand, but the error's Format method will not |  | ||||||
| // return the wrapped error. |  | ||||||
| // |  | ||||||
| // It is invalid to include more than one %w verb or to supply it with an |  | ||||||
| // operand that does not implement the error interface. The %w verb is otherwise |  | ||||||
| // a synonym for %v. |  | ||||||
| func Errorf(format string, a ...interface{}) error { |  | ||||||
| 	format = formatPlusW(format) |  | ||||||
| 	// Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. |  | ||||||
| 	wrap := strings.HasSuffix(format, ": %w") |  | ||||||
| 	idx, format2, ok := parsePercentW(format) |  | ||||||
| 	percentWElsewhere := !wrap && idx >= 0 |  | ||||||
| 	if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { |  | ||||||
| 		err := errorAt(a, len(a)-1) |  | ||||||
| 		if err == nil { |  | ||||||
| 			return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} |  | ||||||
| 		} |  | ||||||
| 		// TODO: this is not entirely correct. The error value could be |  | ||||||
| 		// printed elsewhere in format if it mixes numbered with unnumbered |  | ||||||
| 		// substitutions. With relatively small changes to doPrintf we can |  | ||||||
| 		// have it optionally ignore extra arguments and pass the argument |  | ||||||
| 		// list in its entirety. |  | ||||||
| 		msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) |  | ||||||
| 		frame := Frame{} |  | ||||||
| 		if internal.EnableTrace { |  | ||||||
| 			frame = Caller(1) |  | ||||||
| 		} |  | ||||||
| 		if wrap { |  | ||||||
| 			return &wrapError{msg, err, frame} |  | ||||||
| 		} |  | ||||||
| 		return &noWrapError{msg, err, frame} |  | ||||||
| 	} |  | ||||||
| 	// Support %w anywhere. |  | ||||||
| 	// TODO: don't repeat the wrapped error's message when %w occurs in the middle. |  | ||||||
| 	msg := fmt.Sprintf(format2, a...) |  | ||||||
| 	if idx < 0 { |  | ||||||
| 		return &noWrapError{msg, nil, Caller(1)} |  | ||||||
| 	} |  | ||||||
| 	err := errorAt(a, idx) |  | ||||||
| 	if !ok || err == nil { |  | ||||||
| 		// Too many %ws or argument of %w is not an error. Approximate the Go |  | ||||||
| 		// 1.13 fmt.Errorf message. |  | ||||||
| 		return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} |  | ||||||
| 	} |  | ||||||
| 	frame := Frame{} |  | ||||||
| 	if internal.EnableTrace { |  | ||||||
| 		frame = Caller(1) |  | ||||||
| 	} |  | ||||||
| 	return &wrapError{msg, err, frame} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func errorAt(args []interface{}, i int) error { |  | ||||||
| 	if i < 0 || i >= len(args) { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	err, ok := args[i].(error) |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // formatPlusW is used to avoid the vet check that will barf at %w. |  | ||||||
| func formatPlusW(s string) string { |  | ||||||
| 	return s |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Return the index of the only %w in format, or -1 if none. |  | ||||||
| // Also return a rewritten format string with %w replaced by %v, and |  | ||||||
| // false if there is more than one %w. |  | ||||||
| // TODO: handle "%[N]w". |  | ||||||
| func parsePercentW(format string) (idx int, newFormat string, ok bool) { |  | ||||||
| 	// Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. |  | ||||||
| 	idx = -1 |  | ||||||
| 	ok = true |  | ||||||
| 	n := 0 |  | ||||||
| 	sz := 0 |  | ||||||
| 	var isW bool |  | ||||||
| 	for i := 0; i < len(format); i += sz { |  | ||||||
| 		if format[i] != '%' { |  | ||||||
| 			sz = 1 |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		// "%%" is not a format directive. |  | ||||||
| 		if i+1 < len(format) && format[i+1] == '%' { |  | ||||||
| 			sz = 2 |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		sz, isW = parsePrintfVerb(format[i:]) |  | ||||||
| 		if isW { |  | ||||||
| 			if idx >= 0 { |  | ||||||
| 				ok = false |  | ||||||
| 			} else { |  | ||||||
| 				idx = n |  | ||||||
| 			} |  | ||||||
| 			// "Replace" the last character, the 'w', with a 'v'. |  | ||||||
| 			p := i + sz - 1 |  | ||||||
| 			format = format[:p] + "v" + format[p+1:] |  | ||||||
| 		} |  | ||||||
| 		n++ |  | ||||||
| 	} |  | ||||||
| 	return idx, format, ok |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Parse the printf verb starting with a % at s[0]. |  | ||||||
| // Return how many bytes it occupies and whether the verb is 'w'. |  | ||||||
| func parsePrintfVerb(s string) (int, bool) { |  | ||||||
| 	// Assume only that the directive is a sequence of non-letters followed by a single letter. |  | ||||||
| 	sz := 0 |  | ||||||
| 	var r rune |  | ||||||
| 	for i := 1; i < len(s); i += sz { |  | ||||||
| 		r, sz = utf8.DecodeRuneInString(s[i:]) |  | ||||||
| 		if unicode.IsLetter(r) { |  | ||||||
| 			return i + sz, r == 'w' |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return len(s), false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type noWrapError struct { |  | ||||||
| 	msg   string |  | ||||||
| 	err   error |  | ||||||
| 	frame Frame |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *noWrapError) Error() string { |  | ||||||
| 	return fmt.Sprint(e) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } |  | ||||||
| 
 |  | ||||||
| func (e *noWrapError) FormatError(p Printer) (next error) { |  | ||||||
| 	p.Print(e.msg) |  | ||||||
| 	e.frame.Format(p) |  | ||||||
| 	return e.err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type wrapError struct { |  | ||||||
| 	msg   string |  | ||||||
| 	err   error |  | ||||||
| 	frame Frame |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *wrapError) Error() string { |  | ||||||
| 	return fmt.Sprint(e) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } |  | ||||||
| 
 |  | ||||||
| func (e *wrapError) FormatError(p Printer) (next error) { |  | ||||||
| 	p.Print(e.msg) |  | ||||||
| 	e.frame.Format(p) |  | ||||||
| 	return e.err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *wrapError) Unwrap() error { |  | ||||||
| 	return e.err |  | ||||||
| } |  | ||||||
							
								
								
									
										34
									
								
								vendor/golang.org/x/xerrors/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								vendor/golang.org/x/xerrors/format.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,34 +0,0 @@ | |||||||
| // Copyright 2018 The Go Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a BSD-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
| 
 |  | ||||||
| package xerrors |  | ||||||
| 
 |  | ||||||
| // A Formatter formats error messages. |  | ||||||
| type Formatter interface { |  | ||||||
| 	error |  | ||||||
| 
 |  | ||||||
| 	// FormatError prints the receiver's first error and returns the next error in |  | ||||||
| 	// the error chain, if any. |  | ||||||
| 	FormatError(p Printer) (next error) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // A Printer formats error messages. |  | ||||||
| // |  | ||||||
| // The most common implementation of Printer is the one provided by package fmt |  | ||||||
| // during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message |  | ||||||
| // typically provide their own implementations. |  | ||||||
| type Printer interface { |  | ||||||
| 	// Print appends args to the message output. |  | ||||||
| 	Print(args ...interface{}) |  | ||||||
| 
 |  | ||||||
| 	// Printf writes a formatted string. |  | ||||||
| 	Printf(format string, args ...interface{}) |  | ||||||
| 
 |  | ||||||
| 	// Detail reports whether error detail is requested. |  | ||||||
| 	// After the first call to Detail, all text written to the Printer |  | ||||||
| 	// is formatted as additional detail, or ignored when |  | ||||||
| 	// detail has not been requested. |  | ||||||
| 	// If Detail returns false, the caller can avoid printing the detail at all. |  | ||||||
| 	Detail() bool |  | ||||||
| } |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user