blob: 24a8f0fcd54eb065f94bd440c290b64198f6564f [file] [log] [blame]
// 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))
}
`))