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 | ||||
| *.claims.json | ||||
| *.jwk.json | ||||
| *.jws.json | ||||
| *.jwt.txt | ||||
| 
 | ||||
| xversion.go | ||||
| 
 | ||||
| *_string.go | ||||
| *_vfsdata.go | ||||
| *.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:' | ||||
							
								
								
									
										191
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								README.md
									
									
									
									
									
								
							| @ -1,69 +1,22 @@ | ||||
| # [GoServ](https://git.coolaj86.com/coolaj86/goserv) | ||||
| 
 | ||||
|  | ||||
| # goserv | ||||
| 
 | ||||
| > Boilerplate for how I like to write a backend web service | ||||
| 
 | ||||
| ## 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 | ||||
| export GOFLAGS="-mod=vendor" | ||||
| go mod tidy | ||||
| go 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 | ||||
| GOOS=linux GOARCH=arm64 go build -mod=vendor -o dist/goserv-linux-arm64 . | ||||
| ./goserv run --listen :3000 --serve-path ./overrides | ||||
| ``` | ||||
| 
 | ||||
| #### Run | ||||
| 
 | ||||
| ```bash | ||||
| ./dist/goserv run --listen :3000 --trust-proxy --serve-path ./overrides | ||||
| ``` | ||||
| 
 | ||||
| ## Examples and Config Templates | ||||
| ## Eamples and Config Templates | ||||
| 
 | ||||
| The example files are located in `./examples` | ||||
| 
 | ||||
| @ -71,128 +24,6 @@ The example files are located in `./examples` | ||||
| -   .env (environment variables) | ||||
| -   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 | ||||
| 
 | ||||
| This setup can be run on a VPS, such as Digital Ocean, OVH, or Scaleway | ||||
| @ -249,7 +80,7 @@ sudo env PATH="$PATH" \ | ||||
|     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 | ||||
| 
 | ||||
| ### PostgreSQL (Database) | ||||
| @ -275,14 +106,6 @@ psql 'postgres://postgres:postgres@localhost:5432/postgres' | ||||
| See the Cheat Sheets at https://webinstall.dev/postgres | ||||
| and https://webinstall.dev/serviceman | ||||
| 
 | ||||
| ## Licenses | ||||
| ## License | ||||
| 
 | ||||
| Copyright 2020 The GoServ Authors. 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) | ||||
| Copyright 2020. All rights reserved. | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| // +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 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| // +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 | ||||
|  | ||||
| @ -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(), | ||||
|     "ppid" TEXT NOT NULL, | ||||
|     "email" TEXT NOT NULL, | ||||
|     "verified_at" TIMESTAMPTZ NOT NULL DEFAULT ('0001-01-01 00:00:00' AT TIME ZONE 'UTC'), | ||||
|     "created_at" TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), | ||||
|     "updated_at" TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), | ||||
|     "deleted_at" TIMESTAMPTZ NOT NULL DEFAULT ('0001-01-01 00:00:00' AT TIME ZONE 'UTC') | ||||
|     "verified" BOOL NOT NULL DEFAULT FALSE, | ||||
|     "created_at" TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'), | ||||
|     "updated_at" TIMESTAMP NOT NULL DEFAULT (now() 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_email" ON "authn" ("email"); | ||||
| 
 | ||||
| @ -26,8 +26,8 @@ CREATE TABLE IF NOT EXISTS "events" ( | ||||
|     "table" TEXT NOT NULL, | ||||
|     "record" 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 IF NOT EXISTS "idx_record" ON "events" ("record"); | ||||
| CREATE INDEX IF NOT EXISTS "idx_by" ON "events" ("by"); | ||||
| CREATE INDEX IF NOT EXISTS "idx_record" ON authn ("record"); | ||||
| 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/* 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 | ||||
|     @notApi { | ||||
| @ -24,8 +22,6 @@ example.com { | ||||
|             try_files {path} {path}/ {path}/index.html | ||||
|         } | ||||
|         not path /api/* | ||||
|         not path /.well-known/openid-configuration | ||||
|         not path /.well-known/jwks.json | ||||
|     } | ||||
|     route { | ||||
|       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 generate -mod=vendor ./... | ||||
| go build -mod=vendor . | ||||
| 
 | ||||
|  | ||||
| @ -1,22 +1,2 @@ | ||||
| PORT="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 | ||||
| 
 | ||||
| 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/jmoiron/sqlx v1.2.0 | ||||
| 	github.com/joho/godotenv v1.3.0 | ||||
| 	github.com/lib/pq v1.8.0 | ||||
| 	github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect | ||||
| 	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 | ||||
| ) | ||||
|  | ||||
							
								
								
									
										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/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= | ||||
| 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| 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/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= | ||||
| @ -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/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-20201001230009-b5b87423c93b h1:07IVqnnzaip3TGyl/cy32V5YP3FguWG4BybYDTBNpm0= | ||||
| golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= | ||||
| golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346 h1:hzJjkvxUIF3bSt+v8N5tBQNx/605vszZJ+3XsIamzZo= | ||||
| 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-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= | ||||
| google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= | ||||
| 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 ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.example.com/example/goserv/assets/configfs" | ||||
| 	"git.coolaj86.com/coolaj86/goserv/assets/configfs" | ||||
| 	"github.com/jmoiron/sqlx" | ||||
| 
 | ||||
| 	// pq injects itself into sql as 'postgres' | ||||
| @ -18,14 +15,21 @@ import ( | ||||
| 
 | ||||
| // DB is a concurrency-safe db connection instance | ||||
| var DB *sqlx.DB | ||||
| var firstDBURL PleaseDoubleCheckTheDatabaseURLDontDropProd | ||||
| 
 | ||||
| // Init initializes the database | ||||
| // Init returns a, you guessed it, New Store | ||||
| func Init(pgURL string) error { | ||||
| 	// 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" | ||||
| 	sqlBytes, err := ioutil.ReadAll(f) | ||||
| 	if nil != err { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	ctx, done := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) | ||||
| 	defer done() | ||||
| @ -33,29 +37,6 @@ func Init(pgURL string) error { | ||||
| 	if err := db.PingContext(ctx); nil != 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 { | ||||
| 		return err | ||||
| 	} | ||||
| @ -64,58 +45,3 @@ func Init(pgURL string) error { | ||||
| 
 | ||||
| 	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 | ||||
| 
 | ||||
| import ( | ||||
| 	"compress/flate" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"math/rand" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.example.com/example/goserv/assets" | ||||
| 	"git.example.com/example/goserv/internal/api" | ||||
| 	"git.example.com/example/goserv/internal/db" | ||||
| 	"git.rootprojects.org/root/keypairs" | ||||
| 	"git.coolaj86.com/coolaj86/goserv/assets" | ||||
| 	"git.coolaj86.com/coolaj86/goserv/internal/db" | ||||
| 
 | ||||
| 	"github.com/go-chi/chi" | ||||
| 	"github.com/go-chi/chi/middleware" | ||||
| 
 | ||||
| 	_ "github.com/joho/godotenv/autoload" | ||||
| ) | ||||
| 
 | ||||
| @ -52,9 +45,6 @@ type runOptions struct { | ||||
| 	trustProxy bool | ||||
| 	compress   bool | ||||
| 	static     string | ||||
| 	pub        string | ||||
| 	oidcWL     string | ||||
| 	demo       bool | ||||
| } | ||||
| 
 | ||||
| var runFlags *flag.FlagSet | ||||
| @ -63,32 +53,17 @@ var initFlags *flag.FlagSet | ||||
| var dbURL string | ||||
| 
 | ||||
| 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{} | ||||
| 	runFlags = flag.NewFlagSet("run", flag.ExitOnError) | ||||
| 	runFlags.StringVar( | ||||
| 		&runOpts.listen, "listen", "", | ||||
| 		"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.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(&runOpts.oidcWL, "oidc-whitelist", "", "list of trusted OIDC issuer URLs (ex: Auth0, Google, PocketID) for SSO") | ||||
| 	runFlags.BoolVar(&runOpts.demo, "demo", false, "demo mode enables unauthenticated 'DELETE /api/public/reset' to reset") | ||||
| 	runFlags.StringVar(&dbURL, "database-url", "", | ||||
| 		"database (postgres) connection url (default postgres://postgres:postgres@localhost:5432/postgres)", | ||||
| 	) | ||||
| 	runFlags.StringVar( | ||||
| 		&dbURL, "db-url", "postgres://postgres:postgres@localhost:5432/postgres", | ||||
| 		"database (postgres) connection url") | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| @ -131,19 +106,6 @@ func main() { | ||||
| 				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() | ||||
| 	default: | ||||
| 		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) { | ||||
| 	// TODO url.Parse | ||||
| 	if strings.Contains(connStr, "@localhost/") || strings.Contains(connStr, "@localhost:") { | ||||
| @ -169,96 +225,3 @@ func initDB(connStr string) { | ||||
| 
 | ||||
| 	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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -36,7 +36,10 @@ | ||||
|                         name="email" | ||||
|                         placeholder="email" | ||||
|                     /> | ||||
|           <button class="btn btn-secondary my-2 my-sm-0" type="submit"> | ||||
|                     <button | ||||
|                         class="btn btn-secondary my-2 my-sm-0" | ||||
|                         type="submit" | ||||
|                     > | ||||
|                         Sign in | ||||
|                     </button> | ||||
|                 </form> | ||||
| @ -53,16 +56,19 @@ | ||||
|                             ><h3 class="card-header"> | ||||
|                                 Server Health | ||||
|                                 <form class="js-healthcheck"> | ||||
|                   <button type="submit" class="float-right btn btn-primary"> | ||||
|                                     <button | ||||
|                                         type="submit" | ||||
|                                         class="float-right btn btn-primary" | ||||
|                                     > | ||||
|                                         Check | ||||
|                                     </button> | ||||
|                                 </form> | ||||
|                             </h3></a | ||||
|                         > | ||||
|             <div class="js-pre card-body"> | ||||
|                         <div class="card-body"> | ||||
|                             <h5 class="card-title">Check Server Status</h5> | ||||
|                             <div class="card-text"> | ||||
|                 <pre><code>curl https://example.com/api/public/ping</code></pre> | ||||
|                                 <pre><code>curl https://example.com/api/public/status</code></pre> | ||||
|                                 <pre><code class="js-server-health">-</code></pre> | ||||
|                             </div> | ||||
|                         </div> | ||||
|  | ||||
| @ -41,71 +41,9 @@ | ||||
|         ev.preventDefault(); | ||||
|         ev.stopPropagation(); | ||||
| 
 | ||||
|     window.fetch("/api/public/ping").then(async function (resp) { | ||||
|         window.fetch("/api/public/status").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 | ||||
|     GET  /public/ping                                       Health Check | ||||
|     POST /public/setup                  <= (none)           Bootstrap | ||||
| 
 | ||||
|     # Admin-only | ||||
|     GET  /admin/ping                                        (authenticated) Health Check | ||||
|     GET  /admin/users                      []Users =>       List ALL Users | ||||
| 
 | ||||
|     # 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 | ||||
| 	_ "github.com/shurcooL/vfsgen" | ||||
| 	_ "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