From 370527f88bbfd5be3192a38af3148dbd64f0976f Mon Sep 17 00:00:00 2001 From: Daisuke Maki Date: Fri, 2 Apr 2021 09:53:41 +0900 Subject: [PATCH] Import code from github.com/lestrrat-go/jwx --- .github/workflows/ci.yml | 35 ++++++++++++++++++++++ .github/workflows/lint.yml | 11 +++++++ README.md | 3 +- blackmagic.go | 55 ++++++++++++++++++++++++++++++++++ blackmagic_test.go | 61 ++++++++++++++++++++++++++++++++++++++ go.mod | 8 +++++ go.sum | 13 ++++++++ 7 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/lint.yml create mode 100644 blackmagic.go create mode 100644 blackmagic_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..258e492 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + go: [ '1.15', '1.16' ] + name: Go ${{ matrix.go }} test + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Install Go stable version + if: matrix.go != 'tip' + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go }} + - name: Install Go tip + if: matrix.go == 'tip' + run: | + git clone --depth=1 https://go.googlesource.com/go $HOME/gotip + cd $HOME/gotip/src + ./make.bash + echo "::set-env name=GOROOT::$HOME/gotip" + echo "::add-path::$HOME/gotip/bin" + echo "::add-path::$(go env GOPATH)/bin" + - name: Test + run: go test -v -race ./... + - name: Upload code coverage to codecov + if: matrix.go == '1.16' + uses: codecov/codecov-action@v1 + with: + file: ./coverage.out + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..759c57d --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,11 @@ +name: lint +on: [push, pull_request] +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: golangci/golangci-lint-action@v2 + with: + version: v1.39.0 diff --git a/README.md b/README.md index 736f13c..0356f8a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # blackmagic -Reflect-based black magic + +Reflect-based black magic. YMMV, and use with caution diff --git a/blackmagic.go b/blackmagic.go new file mode 100644 index 0000000..c97eefd --- /dev/null +++ b/blackmagic.go @@ -0,0 +1,55 @@ +package blackmagic + +import ( + "reflect" + + "github.com/pkg/errors" +) + +// AssignIfCompatible is a convenience function to safely +// assign arbitrary values. dst must be a pointer to an +// empty interface, or it must be a pointer to a compatible +// variable type that can hold src. +func AssignIfCompatible(dst, src interface{}) error { + orv := reflect.ValueOf(src) // save this value for error reporting + result := orv + + // t can be a pointer or a slice, and the code will slightly change + // depending on this + var isSlice bool + switch result.Kind() { + case reflect.Ptr: + // no op + case reflect.Slice: + isSlice = true + default: + return errors.Errorf("argument t to AssignIfCompatible must be a pointer or a slice: %T", src) + } + + rv := reflect.ValueOf(dst) + if rv.Kind() != reflect.Ptr { + return errors.Errorf(`argument to AssignIfCompatible() must be a pointer: %T`, dst) + } + + actualDst := rv.Elem() + switch actualDst.Kind() { + case reflect.Interface: + // If it's an interface, we can just assign the pointer to the interface{} + default: + // If it's a pointer to the struct we're looking for, we need to set + // the de-referenced struct + if !isSlice { + result = result.Elem() + } + } + if !result.Type().AssignableTo(actualDst.Type()) { + return errors.Errorf(`argument to AssignIfCompatible() must be compatible with %T (was %T)`, orv.Interface(), dst) + } + + if !actualDst.CanSet() { + return errors.Errorf(`argument to AssignIfCompatible() must be settable`) + } + actualDst.Set(result) + + return nil +} diff --git a/blackmagic_test.go b/blackmagic_test.go new file mode 100644 index 0000000..53ac302 --- /dev/null +++ b/blackmagic_test.go @@ -0,0 +1,61 @@ +package blackmagic_test + +import ( + "testing" + + "github.com/lestrrat-go/blackmagic" + "github.com/stretchr/testify/assert" +) + +func TestAssignment(t *testing.T) { + testcases := []struct { + Name string + Error bool + Value interface{} + Destination func() interface{} + }{ + { + Name: `empty struct`, + Error: true, + Value: struct{}{}, + Destination: func() interface{} { + var v interface{} + return &v + }, + }, + { + Name: `non pointer destination`, + Error: true, + Value: &struct{}{}, + }, + { + Name: `assign empty struct to int`, + Error: true, + Value: &struct{}{}, + Destination: func() interface{} { + var v int + return &v + }, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + var dst interface{} + if dstFunc := tc.Destination; dstFunc != nil { + dst = dstFunc() + } + err := blackmagic.AssignIfCompatible(dst, tc.Value) + if tc.Error { + if !assert.Error(t, err, `blackmagic.AssignIfCompatible should fail`) { + return + } + } else { + if !assert.NoError(t, err, `blackmagic.AssignIfCompatible should succeed`) { + return + } + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8248ac3 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/lestrrat-go/blackmagic + +go 1.16 + +require ( + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.7.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..22c86e5 --- /dev/null +++ b/go.sum @@ -0,0 +1,13 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=