parent
e409e59a54
commit
c4e9739251
@ -0,0 +1,48 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & 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 tests serves a list of tests for tailscale.com/cmd/viewer.
|
||||
package tests |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"inet.af/netaddr" |
||||
) |
||||
|
||||
//go:generate go run tailscale.com/cmd/viewer --type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices
|
||||
|
||||
type StructWithoutPtrs struct { |
||||
Int int |
||||
Pfx netaddr.IPPrefix |
||||
} |
||||
|
||||
type Map struct { |
||||
M map[string]int |
||||
} |
||||
|
||||
type StructWithPtrs struct { |
||||
Value *StructWithoutPtrs |
||||
Int *int |
||||
|
||||
NoCloneValue *StructWithoutPtrs `codegen:"noclone"` |
||||
} |
||||
|
||||
func (v *StructWithPtrs) String() string { return fmt.Sprintf("%v", v.Int) } |
||||
|
||||
func (v *StructWithPtrs) Equal(v2 *StructWithPtrs) bool { |
||||
return v.Value == v2.Value |
||||
} |
||||
|
||||
type StructWithSlices struct { |
||||
Values []StructWithoutPtrs |
||||
ValuePointers []*StructWithoutPtrs |
||||
StructPointers []*StructWithPtrs |
||||
Structs []StructWithPtrs |
||||
Ints []*int |
||||
|
||||
Slice []string |
||||
Prefixes []netaddr.IPPrefix |
||||
Data []byte |
||||
} |
||||
@ -0,0 +1,120 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by tailscale.com/cmd/cloner; DO NOT EDIT.
|
||||
|
||||
package tests |
||||
|
||||
import ( |
||||
"inet.af/netaddr" |
||||
) |
||||
|
||||
// Clone makes a deep copy of StructWithPtrs.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *StructWithPtrs) Clone() *StructWithPtrs { |
||||
if src == nil { |
||||
return nil |
||||
} |
||||
dst := new(StructWithPtrs) |
||||
*dst = *src |
||||
if dst.Value != nil { |
||||
dst.Value = new(StructWithoutPtrs) |
||||
*dst.Value = *src.Value |
||||
} |
||||
if dst.Int != nil { |
||||
dst.Int = new(int) |
||||
*dst.Int = *src.Int |
||||
} |
||||
return dst |
||||
} |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithPtrsCloneNeedsRegeneration = StructWithPtrs(struct { |
||||
Value *StructWithoutPtrs |
||||
Int *int |
||||
NoCloneValue *StructWithoutPtrs |
||||
}{}) |
||||
|
||||
// Clone makes a deep copy of StructWithoutPtrs.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *StructWithoutPtrs) Clone() *StructWithoutPtrs { |
||||
if src == nil { |
||||
return nil |
||||
} |
||||
dst := new(StructWithoutPtrs) |
||||
*dst = *src |
||||
return dst |
||||
} |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithoutPtrsCloneNeedsRegeneration = StructWithoutPtrs(struct { |
||||
Int int |
||||
Pfx netaddr.IPPrefix |
||||
}{}) |
||||
|
||||
// Clone makes a deep copy of Map.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Map) Clone() *Map { |
||||
if src == nil { |
||||
return nil |
||||
} |
||||
dst := new(Map) |
||||
*dst = *src |
||||
if dst.M != nil { |
||||
dst.M = map[string]int{} |
||||
for k, v := range src.M { |
||||
dst.M[k] = v |
||||
} |
||||
} |
||||
return dst |
||||
} |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _MapCloneNeedsRegeneration = Map(struct { |
||||
M map[string]int |
||||
}{}) |
||||
|
||||
// Clone makes a deep copy of StructWithSlices.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *StructWithSlices) Clone() *StructWithSlices { |
||||
if src == nil { |
||||
return nil |
||||
} |
||||
dst := new(StructWithSlices) |
||||
*dst = *src |
||||
dst.Values = append(src.Values[:0:0], src.Values...) |
||||
dst.ValuePointers = make([]*StructWithoutPtrs, len(src.ValuePointers)) |
||||
for i := range dst.ValuePointers { |
||||
dst.ValuePointers[i] = src.ValuePointers[i].Clone() |
||||
} |
||||
dst.StructPointers = make([]*StructWithPtrs, len(src.StructPointers)) |
||||
for i := range dst.StructPointers { |
||||
dst.StructPointers[i] = src.StructPointers[i].Clone() |
||||
} |
||||
dst.Structs = make([]StructWithPtrs, len(src.Structs)) |
||||
for i := range dst.Structs { |
||||
dst.Structs[i] = *src.Structs[i].Clone() |
||||
} |
||||
dst.Ints = make([]*int, len(src.Ints)) |
||||
for i := range dst.Ints { |
||||
x := *src.Ints[i] |
||||
dst.Ints[i] = &x |
||||
} |
||||
dst.Slice = append(src.Slice[:0:0], src.Slice...) |
||||
dst.Prefixes = append(src.Prefixes[:0:0], src.Prefixes...) |
||||
dst.Data = append(src.Data[:0:0], src.Data...) |
||||
return dst |
||||
} |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithSlicesCloneNeedsRegeneration = StructWithSlices(struct { |
||||
Values []StructWithoutPtrs |
||||
ValuePointers []*StructWithoutPtrs |
||||
StructPointers []*StructWithPtrs |
||||
Structs []StructWithPtrs |
||||
Ints []*int |
||||
Slice []string |
||||
Prefixes []netaddr.IPPrefix |
||||
Data []byte |
||||
}{}) |
||||
@ -0,0 +1,268 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by tailscale/cmd/viewer; DO NOT EDIT.
|
||||
|
||||
package tests |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
|
||||
"go4.org/mem" |
||||
"inet.af/netaddr" |
||||
"tailscale.com/types/views" |
||||
) |
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices
|
||||
|
||||
// View returns a readonly view of StructWithPtrs.
|
||||
func (p *StructWithPtrs) View() StructWithPtrsView { |
||||
return StructWithPtrsView{ж: p} |
||||
} |
||||
|
||||
// StructWithPtrsView provides a read-only view over StructWithPtrs.
|
||||
//
|
||||
// Its methods should only be called if `Valid()` returns true.
|
||||
type StructWithPtrsView struct { |
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
// It is named distinctively to make you think of how dangerous it is to escape
|
||||
// to callers. You must not let callers be able to mutate it.
|
||||
ж *StructWithPtrs |
||||
} |
||||
|
||||
// Valid reports whether underlying value is non-nil.
|
||||
func (v StructWithPtrsView) Valid() bool { return v.ж != nil } |
||||
|
||||
// AsStruct returns a clone of the underlying value which aliases no memory with
|
||||
// the original.
|
||||
func (v StructWithPtrsView) AsStruct() *StructWithPtrs { |
||||
if v.ж == nil { |
||||
return nil |
||||
} |
||||
return v.ж.Clone() |
||||
} |
||||
|
||||
func (v StructWithPtrsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } |
||||
|
||||
func (v *StructWithPtrsView) UnmarshalJSON(b []byte) error { |
||||
if v.ж != nil { |
||||
return errors.New("already initialized") |
||||
} |
||||
if len(b) == 0 { |
||||
return nil |
||||
} |
||||
var x StructWithPtrs |
||||
if err := json.Unmarshal(b, &x); err != nil { |
||||
return err |
||||
} |
||||
v.ж = &x |
||||
return nil |
||||
} |
||||
|
||||
func (v StructWithPtrsView) Value() *StructWithoutPtrs { |
||||
if v.ж.Value == nil { |
||||
return nil |
||||
} |
||||
x := *v.ж.Value |
||||
return &x |
||||
} |
||||
|
||||
func (v StructWithPtrsView) Int() *int { |
||||
if v.ж.Int == nil { |
||||
return nil |
||||
} |
||||
x := *v.ж.Int |
||||
return &x |
||||
} |
||||
|
||||
func (v StructWithPtrsView) NoCloneValue() *StructWithoutPtrs { return v.ж.NoCloneValue } |
||||
func (v StructWithPtrsView) String() string { return v.ж.String() } |
||||
func (v StructWithPtrsView) Equal(v2 StructWithPtrsView) bool { return v.ж.Equal(v2.ж) } |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithPtrsViewNeedsRegeneration = StructWithPtrs(struct { |
||||
Value *StructWithoutPtrs |
||||
Int *int |
||||
NoCloneValue *StructWithoutPtrs |
||||
}{}) |
||||
|
||||
// View returns a readonly view of StructWithoutPtrs.
|
||||
func (p *StructWithoutPtrs) View() StructWithoutPtrsView { |
||||
return StructWithoutPtrsView{ж: p} |
||||
} |
||||
|
||||
// StructWithoutPtrsView provides a read-only view over StructWithoutPtrs.
|
||||
//
|
||||
// Its methods should only be called if `Valid()` returns true.
|
||||
type StructWithoutPtrsView struct { |
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
// It is named distinctively to make you think of how dangerous it is to escape
|
||||
// to callers. You must not let callers be able to mutate it.
|
||||
ж *StructWithoutPtrs |
||||
} |
||||
|
||||
// Valid reports whether underlying value is non-nil.
|
||||
func (v StructWithoutPtrsView) Valid() bool { return v.ж != nil } |
||||
|
||||
// AsStruct returns a clone of the underlying value which aliases no memory with
|
||||
// the original.
|
||||
func (v StructWithoutPtrsView) AsStruct() *StructWithoutPtrs { |
||||
if v.ж == nil { |
||||
return nil |
||||
} |
||||
return v.ж.Clone() |
||||
} |
||||
|
||||
func (v StructWithoutPtrsView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } |
||||
|
||||
func (v *StructWithoutPtrsView) UnmarshalJSON(b []byte) error { |
||||
if v.ж != nil { |
||||
return errors.New("already initialized") |
||||
} |
||||
if len(b) == 0 { |
||||
return nil |
||||
} |
||||
var x StructWithoutPtrs |
||||
if err := json.Unmarshal(b, &x); err != nil { |
||||
return err |
||||
} |
||||
v.ж = &x |
||||
return nil |
||||
} |
||||
|
||||
func (v StructWithoutPtrsView) Int() int { return v.ж.Int } |
||||
func (v StructWithoutPtrsView) Pfx() netaddr.IPPrefix { return v.ж.Pfx } |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithoutPtrsViewNeedsRegeneration = StructWithoutPtrs(struct { |
||||
Int int |
||||
Pfx netaddr.IPPrefix |
||||
}{}) |
||||
|
||||
// View returns a readonly view of Map.
|
||||
func (p *Map) View() MapView { |
||||
return MapView{ж: p} |
||||
} |
||||
|
||||
// MapView provides a read-only view over Map.
|
||||
//
|
||||
// Its methods should only be called if `Valid()` returns true.
|
||||
type MapView struct { |
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
// It is named distinctively to make you think of how dangerous it is to escape
|
||||
// to callers. You must not let callers be able to mutate it.
|
||||
ж *Map |
||||
} |
||||
|
||||
// Valid reports whether underlying value is non-nil.
|
||||
func (v MapView) Valid() bool { return v.ж != nil } |
||||
|
||||
// AsStruct returns a clone of the underlying value which aliases no memory with
|
||||
// the original.
|
||||
func (v MapView) AsStruct() *Map { |
||||
if v.ж == nil { |
||||
return nil |
||||
} |
||||
return v.ж.Clone() |
||||
} |
||||
|
||||
func (v MapView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } |
||||
|
||||
func (v *MapView) UnmarshalJSON(b []byte) error { |
||||
if v.ж != nil { |
||||
return errors.New("already initialized") |
||||
} |
||||
if len(b) == 0 { |
||||
return nil |
||||
} |
||||
var x Map |
||||
if err := json.Unmarshal(b, &x); err != nil { |
||||
return err |
||||
} |
||||
v.ж = &x |
||||
return nil |
||||
} |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _MapViewNeedsRegeneration = Map(struct { |
||||
M map[string]int |
||||
}{}) |
||||
|
||||
// View returns a readonly view of StructWithSlices.
|
||||
func (p *StructWithSlices) View() StructWithSlicesView { |
||||
return StructWithSlicesView{ж: p} |
||||
} |
||||
|
||||
// StructWithSlicesView provides a read-only view over StructWithSlices.
|
||||
//
|
||||
// Its methods should only be called if `Valid()` returns true.
|
||||
type StructWithSlicesView struct { |
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
// It is named distinctively to make you think of how dangerous it is to escape
|
||||
// to callers. You must not let callers be able to mutate it.
|
||||
ж *StructWithSlices |
||||
} |
||||
|
||||
// Valid reports whether underlying value is non-nil.
|
||||
func (v StructWithSlicesView) Valid() bool { return v.ж != nil } |
||||
|
||||
// AsStruct returns a clone of the underlying value which aliases no memory with
|
||||
// the original.
|
||||
func (v StructWithSlicesView) AsStruct() *StructWithSlices { |
||||
if v.ж == nil { |
||||
return nil |
||||
} |
||||
return v.ж.Clone() |
||||
} |
||||
|
||||
func (v StructWithSlicesView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } |
||||
|
||||
func (v *StructWithSlicesView) UnmarshalJSON(b []byte) error { |
||||
if v.ж != nil { |
||||
return errors.New("already initialized") |
||||
} |
||||
if len(b) == 0 { |
||||
return nil |
||||
} |
||||
var x StructWithSlices |
||||
if err := json.Unmarshal(b, &x); err != nil { |
||||
return err |
||||
} |
||||
v.ж = &x |
||||
return nil |
||||
} |
||||
|
||||
func (v StructWithSlicesView) Values() views.Slice[StructWithoutPtrs] { |
||||
return views.SliceOf(v.ж.Values) |
||||
} |
||||
func (v StructWithSlicesView) ValuePointers() views.SliceView[*StructWithoutPtrs, StructWithoutPtrsView] { |
||||
return views.SliceOfViews[*StructWithoutPtrs, StructWithoutPtrsView](v.ж.ValuePointers) |
||||
} |
||||
func (v StructWithSlicesView) StructPointers() views.SliceView[*StructWithPtrs, StructWithPtrsView] { |
||||
return views.SliceOfViews[*StructWithPtrs, StructWithPtrsView](v.ж.StructPointers) |
||||
} |
||||
func (v StructWithSlicesView) Structs() StructWithPtrs { panic("unsupported") } |
||||
func (v StructWithSlicesView) Ints() *int { panic("unsupported") } |
||||
func (v StructWithSlicesView) Slice() views.Slice[string] { return views.SliceOf(v.ж.Slice) } |
||||
func (v StructWithSlicesView) Prefixes() views.IPPrefixSlice { |
||||
return views.IPPrefixSliceOf(v.ж.Prefixes) |
||||
} |
||||
func (v StructWithSlicesView) Data() mem.RO { return mem.B(v.ж.Data) } |
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct { |
||||
Values []StructWithoutPtrs |
||||
ValuePointers []*StructWithoutPtrs |
||||
StructPointers []*StructWithPtrs |
||||
Structs []StructWithPtrs |
||||
Ints []*int |
||||
Slice []string |
||||
Prefixes []netaddr.IPPrefix |
||||
Data []byte |
||||
}{}) |
||||
@ -0,0 +1,316 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Viewer is a tool to automate the creation of "view" wrapper types that
|
||||
// provide read-only accessor methods to underlying fields.
|
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"flag" |
||||
"fmt" |
||||
"go/types" |
||||
"html/template" |
||||
"log" |
||||
"os" |
||||
"strings" |
||||
|
||||
"tailscale.com/util/codegen" |
||||
) |
||||
|
||||
const viewTemplateStr = `{{define "common"}} |
||||
// View returns a readonly view of {{.StructName}}.
|
||||
func (p *{{.StructName}}) View() {{.ViewName}} { |
||||
return {{.ViewName}}{ж: p} |
||||
} |
||||
|
||||
// {{.ViewName}} provides a read-only view over {{.StructName}}.
|
||||
//
|
||||
// Its methods should only be called if ` + "`Valid()`" + ` returns true.
|
||||
type {{.ViewName}} struct { |
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
// It is named distinctively to make you think of how dangerous it is to escape
|
||||
// to callers. You must not let callers be able to mutate it.
|
||||
ж *{{.StructName}} |
||||
} |
||||
|
||||
// Valid reports whether underlying value is non-nil.
|
||||
func (v {{.ViewName}}) Valid() bool { return v.ж != nil } |
||||
|
||||
// AsStruct returns a clone of the underlying value which aliases no memory with
|
||||
// the original.
|
||||
func (v {{.ViewName}}) AsStruct() *{{.StructName}}{
|
||||
if v.ж == nil { |
||||
return nil |
||||
} |
||||
return v.ж.Clone() |
||||
} |
||||
|
||||
func (v {{.ViewName}}) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } |
||||
|
||||
func (v *{{.ViewName}}) UnmarshalJSON(b []byte) error { |
||||
if v.ж != nil { |
||||
return errors.New("already initialized") |
||||
} |
||||
if len(b) == 0 { |
||||
return nil |
||||
} |
||||
var x {{.StructName}} |
||||
if err := json.Unmarshal(b, &x); err != nil { |
||||
return err |
||||
} |
||||
v.ж=&x |
||||
return nil |
||||
} |
||||
|
||||
{{end}} |
||||
{{define "valueField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} { return v.ж.{{.FieldName}} } |
||||
{{end}} |
||||
{{define "byteSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() mem.RO { return mem.B(v.ж.{{.FieldName}}) } |
||||
{{end}} |
||||
{{define "ipPrefixSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.IPPrefixSlice { return views.IPPrefixSliceOf(v.ж.{{.FieldName}}) } |
||||
{{end}} |
||||
{{define "sliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.Slice[{{.FieldType}}] { return views.SliceOf(v.ж.{{.FieldName}}) } |
||||
{{end}} |
||||
{{define "viewSliceField"}}func (v {{.ViewName}}) {{.FieldName}}() views.SliceView[{{.FieldType}},{{.FieldViewName}}] { return views.SliceOfViews[{{.FieldType}},{{.FieldViewName}}](v.ж.{{.FieldName}}) } |
||||
{{end}} |
||||
{{define "viewField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}}View { return v.ж.{{.FieldName}}.View() } |
||||
{{end}} |
||||
{{define "valuePointerField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} { |
||||
if v.ж.{{.FieldName}} == nil { |
||||
return nil |
||||
} |
||||
x := *v.ж.{{.FieldName}} |
||||
return &x |
||||
} |
||||
|
||||
{{end}} |
||||
{{define "mapField"}} |
||||
// Unsupported, panics.
|
||||
func(v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} {panic("unsupported")} |
||||
{{end}} |
||||
{{define "unsupportedField"}}func(v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} {panic("unsupported")} |
||||
{{end}} |
||||
{{define "stringFunc"}}func(v {{.ViewName}}) String() string { return v.ж.String() } |
||||
{{end}} |
||||
{{define "equalFunc"}}func(v {{.ViewName}}) Equal(v2 {{.ViewName}}) bool { return v.ж.Equal(v2.ж) } |
||||
{{end}} |
||||
` |
||||
|
||||
var viewTemplate *template.Template |
||||
|
||||
func init() { |
||||
viewTemplate = template.Must(template.New("view").Parse(viewTemplateStr)) |
||||
} |
||||
|
||||
func requiresCloning(t types.Type) (shallow, deep bool, base types.Type) { |
||||
switch v := t.(type) { |
||||
case *types.Pointer: |
||||
_, deep, base = requiresCloning(v.Elem()) |
||||
return true, deep, base |
||||
case *types.Slice: |
||||
_, deep, base = requiresCloning(v.Elem()) |
||||
return true, deep, base |
||||
} |
||||
p := codegen.ContainsPointers(t) |
||||
return p, p, t |
||||
} |
||||
|
||||
func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thisPkg *types.Package) { |
||||
t, ok := typ.Underlying().(*types.Struct) |
||||
if !ok || codegen.IsViewType(t) { |
||||
return |
||||
} |
||||
it.Import("encoding/json") |
||||
it.Import("errors") |
||||
|
||||
args := struct { |
||||
StructName string |
||||
ViewName string |
||||
FieldName string |
||||
FieldType string |
||||
FieldViewName string |
||||
}{ |
||||
StructName: typ.Obj().Name(), |
||||
ViewName: typ.Obj().Name() + "View", |
||||
} |
||||
|
||||
writeTemplate := func(name string) { |
||||
if err := viewTemplate.ExecuteTemplate(buf, name, args); err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
} |
||||
writeTemplate("common") |
||||
for i := 0; i < t.NumFields(); i++ { |
||||
f := t.Field(i) |
||||
fname := f.Name() |
||||
if !f.Exported() { |
||||
continue |
||||
} |
||||
args.FieldName = fname |
||||
fieldType := f.Type() |
||||
if codegen.IsInvalid(fieldType) { |
||||
continue |
||||
} |
||||
if !codegen.ContainsPointers(fieldType) || codegen.IsViewType(fieldType) || codegen.HasNoClone(t.Tag(i)) { |
||||
args.FieldType = it.QualifiedName(fieldType) |
||||
writeTemplate("valueField") |
||||
continue |
||||
} |
||||
switch underlying := fieldType.Underlying().(type) { |
||||
case *types.Slice: |
||||
slice := underlying |
||||
elem := slice.Elem() |
||||
args.FieldType = it.QualifiedName(elem) |
||||
switch elem.String() { |
||||
case "byte": |
||||
it.Import("go4.org/mem") |
||||
writeTemplate("byteSliceField") |
||||
case "inet.af/netaddr.IPPrefix": |
||||
it.Import("tailscale.com/types/views") |
||||
writeTemplate("ipPrefixSliceField") |
||||
default: |
||||
it.Import("tailscale.com/types/views") |
||||
shallow, deep, base := requiresCloning(elem) |
||||
if deep { |
||||
if _, isPtr := elem.(*types.Pointer); isPtr { |
||||
args.FieldViewName = it.QualifiedName(base) + "View" |
||||
writeTemplate("viewSliceField") |
||||
} else { |
||||
writeTemplate("unsupportedField") |
||||
} |
||||
continue |
||||
} else if shallow { |
||||
if _, isBasic := base.(*types.Basic); isBasic { |
||||
writeTemplate("unsupportedField") |
||||
} else { |
||||
args.FieldViewName = it.QualifiedName(base) + "View" |
||||
writeTemplate("viewSliceField") |
||||
} |
||||
continue |
||||
} |
||||
writeTemplate("sliceField") |
||||
} |
||||
continue |
||||
case *types.Struct: |
||||
strucT := underlying |
||||
args.FieldType = it.QualifiedName(fieldType) |
||||
if codegen.ContainsPointers(strucT) { |
||||
writeTemplate("viewField") |
||||
continue |
||||
} |
||||
writeTemplate("valueField") |
||||
continue |
||||
case *types.Map: |
||||
// TODO(maisem): support this.
|
||||
// args.FieldType = importedName(ft)
|
||||
// writeTemplate("mapField")
|
||||
continue |
||||
case *types.Pointer: |
||||
ptr := underlying |
||||
_, deep, base := requiresCloning(ptr) |
||||
if deep { |
||||
args.FieldType = it.QualifiedName(base) |
||||
writeTemplate("viewField") |
||||
} else { |
||||
args.FieldType = it.QualifiedName(ptr) |
||||
writeTemplate("valuePointerField") |
||||
} |
||||
continue |
||||
} |
||||
writeTemplate("unsupportedField") |
||||
} |
||||
for i := 0; i < typ.NumMethods(); i++ { |
||||
f := typ.Method(i) |
||||
if !f.Exported() { |
||||
continue |
||||
} |
||||
sig, ok := f.Type().(*types.Signature) |
||||
if !ok { |
||||
continue |
||||
} |
||||
|
||||
switch f.Name() { |
||||
case "Clone", "View": |
||||
continue // "AsStruct"
|
||||
case "String": |
||||
writeTemplate("stringFunc") |
||||
continue |
||||
case "Equal": |
||||
if sig.Results().Len() == 1 && sig.Results().At(0).Type().String() == "bool" { |
||||
writeTemplate("equalFunc") |
||||
continue |
||||
} |
||||
} |
||||
} |
||||
fmt.Fprintf(buf, "\n") |
||||
buf.Write(codegen.AssertStructUnchanged(t, args.StructName, "View", it)) |
||||
} |
||||
|
||||
var ( |
||||
flagTypes = flag.String("type", "", "comma-separated list of types; required") |
||||
flagBuildTags = flag.String("tags", "", "compiler build tags to apply") |
||||
flagCloneFunc = flag.Bool("clonefunc", false, "add a top-level Clone func") |
||||
) |
||||
|
||||
func main() { |
||||
log.SetFlags(0) |
||||
log.SetPrefix("viewer: ") |
||||
flag.Parse() |
||||
if len(*flagTypes) == 0 { |
||||
flag.Usage() |
||||
os.Exit(2) |
||||
} |
||||
typeNames := strings.Split(*flagTypes, ",") |
||||
|
||||
var flagArgs []string |
||||
flagArgs = append(flagArgs, fmt.Sprintf("-clonefunc=%v", *flagCloneFunc)) |
||||
if *flagTypes != "" { |
||||
flagArgs = append(flagArgs, "-type="+*flagTypes) |
||||
} |
||||
if *flagBuildTags != "" { |
||||
flagArgs = append(flagArgs, "-tags="+*flagBuildTags) |
||||
} |
||||
pkg, namedTypes, err := codegen.LoadTypes(*flagBuildTags, ".") |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
it := codegen.NewImportTracker(pkg.Types) |
||||
|
||||
buf := new(bytes.Buffer) |
||||
fmt.Fprintf(buf, `//go:generate go run tailscale.com/cmd/cloner %s`, strings.Join(flagArgs, " ")) |
||||
fmt.Fprintln(buf) |
||||
runCloner := false |
||||
for _, typeName := range typeNames { |
||||
typ, ok := namedTypes[typeName] |
||||
if !ok { |
||||
log.Fatalf("could not find type %s", typeName) |
||||
} |
||||
var hasClone bool |
||||
for i, n := 0, typ.NumMethods(); i < n; i++ { |
||||
if typ.Method(i).Name() == "Clone" { |
||||
hasClone = true |
||||
break |
||||
} |
||||
} |
||||
if !hasClone { |
||||
runCloner = true |
||||
} |
||||
genView(buf, it, typ, pkg.Types) |
||||
} |
||||
out := pkg.Name + "_view.go" |
||||
if err := codegen.WritePackageFile("tailscale/cmd/viewer", pkg, out, it, buf); err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
if runCloner { |
||||
// When a new pacakge is added or when existing generated files have
|
||||
// been deleted, we might run into a case where tailscale.com/cmd/cloner
|
||||
// has not run yet. We detect this by verifying that all the structs we
|
||||
// interacted with have had Clone method already generated. If they
|
||||
// haven't we ask the caller to rerun generation again so that those get
|
||||
// generated.
|
||||
log.Printf("%v requires regeneration. Please run go generate again", pkg.Name+"_clone.go") |
||||
} |
||||
} |
||||
Loading…
Reference in new issue