Generate atomic Value wrappers automatically
Rather than hand-writing wrappers around atomic.Value, generate them automatically from the same template. The generator is at internal/gen-valuewrapper. The generator correctly handles generating wrapper structs for nillable types.
This commit is contained in:
@@ -15,3 +15,4 @@ coverage:
|
||||
|
||||
ignore:
|
||||
- /internal/gen-atomicint/
|
||||
- /internal/gen-valuewrapper/
|
||||
|
||||
6
Makefile
6
Makefile
@@ -3,6 +3,7 @@ export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
|
||||
GEN_VALUEWRAPPER = $(GOBIN)/gen-valuewrapper
|
||||
|
||||
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
|
||||
|
||||
@@ -23,6 +24,9 @@ gofmt:
|
||||
$(GOLINT):
|
||||
go install golang.org/x/lint/golint
|
||||
|
||||
$(GEN_VALUEWRAPPER): $(wildcard ./internal/gen-valuewrapper/*)
|
||||
go build -o $@ ./internal/gen-valuewrapper
|
||||
|
||||
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
|
||||
go build -o $@ ./internal/gen-atomicint
|
||||
|
||||
@@ -39,7 +43,7 @@ cover:
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(GEN_ATOMICINT)
|
||||
generate: $(GEN_ATOMICINT) $(GEN_VALUEWRAPPER)
|
||||
go generate ./...
|
||||
|
||||
.PHONY: generatenodirty
|
||||
|
||||
50
error.go
50
error.go
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
// Copyright (c) 2020 Uber Technologies, 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
|
||||
@@ -20,36 +20,40 @@
|
||||
|
||||
package atomic
|
||||
|
||||
// Error is an atomic type-safe wrapper around Value for errors
|
||||
// Error is an atomic type-safe wrapper for error values.
|
||||
type Error struct{ v Value }
|
||||
|
||||
// errorHolder is non-nil holder for error object.
|
||||
// atomic.Value panics on saving nil object, so err object needs to be
|
||||
// wrapped with valid object first.
|
||||
type errorHolder struct{ err error }
|
||||
type storedError struct{ Value error }
|
||||
|
||||
// NewError creates new atomic error object
|
||||
func NewError(err error) *Error {
|
||||
e := &Error{}
|
||||
if err != nil {
|
||||
e.Store(err)
|
||||
}
|
||||
return e
|
||||
func wrapError(v error) storedError {
|
||||
return storedError{v}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped error
|
||||
func (e *Error) Load() error {
|
||||
v := e.v.Load()
|
||||
func unwrapError(v storedError) error {
|
||||
return v.Value
|
||||
}
|
||||
|
||||
// NewError creates a new Error.
|
||||
func NewError(v error) *Error {
|
||||
x := &Error{}
|
||||
if v != nil {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped error.
|
||||
func (x *Error) Load() error {
|
||||
v := x.v.Load()
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
eh := v.(errorHolder)
|
||||
return eh.err
|
||||
return unwrapError(v.(storedError))
|
||||
}
|
||||
|
||||
// Store atomically stores error.
|
||||
// NOTE: a holder object is allocated on each Store call.
|
||||
func (e *Error) Store(err error) {
|
||||
e.v.Store(errorHolder{err: err})
|
||||
// Store atomically stores the passed error.
|
||||
//
|
||||
// NOTE: This will cause an allocation.
|
||||
func (x *Error) Store(v error) {
|
||||
x.v.Store(wrapError(v))
|
||||
}
|
||||
|
||||
3
gen.go
3
gen.go
@@ -24,3 +24,6 @@ package atomic
|
||||
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
||||
|
||||
//go:generate bin/gen-valuewrapper -name=String -type=string -zero="" -file=string.go
|
||||
//go:generate bin/gen-valuewrapper -name=Error -type=error -zero=nil -file=error.go
|
||||
|
||||
171
internal/gen-valuewrapper/main.go
Normal file
171
internal/gen-valuewrapper/main.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2020 Uber Technologies, 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
if err := run(os.Args[1:]); err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
var opts struct {
|
||||
Name string
|
||||
Type string
|
||||
Zero string
|
||||
File string
|
||||
}
|
||||
|
||||
flag := flag.NewFlagSet("gen-atomicint", flag.ContinueOnError)
|
||||
|
||||
flag.StringVar(&opts.Name, "name", "", "name of the generated type (e.g. Int32)")
|
||||
flag.StringVar(&opts.Type, "type", "", "name of the wrapped type (e.g. int32)")
|
||||
flag.StringVar(&opts.Zero, "zero", "", "zero value of the wrapped type (e.g. nil)")
|
||||
flag.StringVar(&opts.File, "file", "", "output file path (default: stdout)")
|
||||
|
||||
if err := flag.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(opts.Name) == 0 || len(opts.Type) == 0 || len(opts.Zero) == 0 {
|
||||
return errors.New("flags -name, -type, and -zero are required")
|
||||
}
|
||||
|
||||
var w io.Writer = os.Stdout
|
||||
if file := opts.File; len(file) > 0 {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create %q: %v", file, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w = f
|
||||
}
|
||||
|
||||
data := struct {
|
||||
Name string
|
||||
Type string
|
||||
Zero string
|
||||
Nillable bool
|
||||
}{
|
||||
Name: opts.Name,
|
||||
Type: opts.Type,
|
||||
Zero: opts.Zero,
|
||||
Nillable: opts.Zero == "nil",
|
||||
}
|
||||
|
||||
var buff bytes.Buffer
|
||||
if err := _tmpl.Execute(&buff, data); err != nil {
|
||||
return fmt.Errorf("render template: %v", err)
|
||||
}
|
||||
|
||||
bs, err := format.Source(buff.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("reformat source: %v", err)
|
||||
}
|
||||
|
||||
_, err = w.Write(bs)
|
||||
return err
|
||||
}
|
||||
|
||||
var _tmpl = template.Must(template.New("int.go").Parse(`// Copyright (c) 2020 Uber Technologies, 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// {{ .Name }} is an atomic type-safe wrapper for {{ .Type }} values.
|
||||
type {{ .Name }} struct{ v Value }
|
||||
|
||||
{{/* atomic.Value panics for nil values. Generate a wrapper for these types. */}}
|
||||
{{ $stored := .Type }}
|
||||
{{ $wrap := .Type }}
|
||||
{{ $unwrap := .Type }}
|
||||
{{ if .Nillable -}}
|
||||
{{ $stored = printf "stored%s" .Name }}
|
||||
{{ $wrap = printf "wrap%s" .Name }}
|
||||
{{ $unwrap = printf "unwrap%s" .Name }}
|
||||
|
||||
type {{ $stored }} struct{ Value {{ .Type }} }
|
||||
|
||||
func {{ $wrap }}(v {{ .Type }}) {{ $stored }} {
|
||||
return {{ $stored }}{v}
|
||||
}
|
||||
|
||||
func {{ $unwrap }}(v {{ $stored }}) {{ .Type }} {
|
||||
return v.Value
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// New{{ .Name }} creates a new {{ .Name }}.
|
||||
func New{{ .Name }}(v {{ .Type }}) *{{ .Name }} {
|
||||
x := &{{ .Name }}{}
|
||||
if v != {{ .Zero }} {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped {{ .Type }}.
|
||||
func (x *{{ .Name }}) Load() {{ .Type }} {
|
||||
v := x.v.Load()
|
||||
if v == nil {
|
||||
return {{ .Zero }}
|
||||
}
|
||||
return {{ $unwrap }}(v.({{ $stored }}))
|
||||
}
|
||||
|
||||
// Store atomically stores the passed {{ .Type }}.
|
||||
//
|
||||
// NOTE: This will cause an allocation.
|
||||
func (x *{{ .Name }}) Store(v {{ .Type }}) {
|
||||
x.v.Store({{ $wrap }}(v))
|
||||
}
|
||||
`))
|
||||
45
string.go
45
string.go
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2016-2020 Uber Technologies, Inc.
|
||||
// Copyright (c) 2020 Uber Technologies, 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
|
||||
@@ -20,45 +20,30 @@
|
||||
|
||||
package atomic
|
||||
|
||||
// String is an atomic type-safe wrapper around Value for strings.
|
||||
// String is an atomic type-safe wrapper for string values.
|
||||
type String struct{ v Value }
|
||||
|
||||
// NewString creates a String.
|
||||
func NewString(str string) *String {
|
||||
s := &String{}
|
||||
if str != "" {
|
||||
s.Store(str)
|
||||
// NewString creates a new String.
|
||||
func NewString(v string) *String {
|
||||
x := &String{}
|
||||
if v != "" {
|
||||
x.Store(v)
|
||||
}
|
||||
return s
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped string.
|
||||
func (s *String) Load() string {
|
||||
v := s.v.Load()
|
||||
func (x *String) Load() string {
|
||||
v := x.v.Load()
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return v.(string)
|
||||
}
|
||||
|
||||
// MarshalText encodes the wrapped string into a textual form.
|
||||
//
|
||||
// This makes it encodable as JSON, YAML, XML, and more.
|
||||
func (s *String) MarshalText() ([]byte, error) {
|
||||
return []byte(s.Load()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||
//
|
||||
// This makes it decodable from JSON, YAML, XML, and more.
|
||||
func (s *String) UnmarshalText(b []byte) error {
|
||||
s.Store(string(b))
|
||||
return nil
|
||||
return string(v.(string))
|
||||
}
|
||||
|
||||
// Store atomically stores the passed string.
|
||||
// Note: Converting the string to an interface{} to store in the Value
|
||||
// requires an allocation.
|
||||
func (s *String) Store(str string) {
|
||||
s.v.Store(str)
|
||||
//
|
||||
// NOTE: This will cause an allocation.
|
||||
func (x *String) Store(v string) {
|
||||
x.v.Store(string(v))
|
||||
}
|
||||
|
||||
36
string_ext.go
Normal file
36
string_ext.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2020 Uber Technologies, 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// MarshalText encodes the wrapped string into a textual form.
|
||||
//
|
||||
// This makes it encodable as JSON, YAML, XML, and more.
|
||||
func (s *String) MarshalText() ([]byte, error) {
|
||||
return []byte(s.Load()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||
//
|
||||
// This makes it decodable from JSON, YAML, XML, and more.
|
||||
func (s *String) UnmarshalText(b []byte) error {
|
||||
s.Store(string(b))
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user