cmd/viewer: add view-only option

This PR adds a --view-only-type flag to viewer to allow
a type to be generated without a cloner.
This is made available to make the few cases where writing
a custom cloner is more practical, for example if the field
is an interface.

Updates corp/#16789

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2024-01-23 13:23:23 +01:00
parent 0e2cb76abe
commit bbd65862d7
No known key found for this signature in database
3 changed files with 79 additions and 4 deletions

View File

@ -9,7 +9,7 @@ import (
"net/netip"
)
//go:generate go run tailscale.com/cmd/viewer --type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices,OnlyGetClone,StructWithEmbedded --clone-only-type=OnlyGetClone
//go:generate go run tailscale.com/cmd/viewer --type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices,OnlyGetClone,OnlyGetView,StructWithEmbedded --clone-only-type=OnlyGetClone --view-only-type=OnlyGetView
type StructWithoutPtrs struct {
Int int
@ -62,6 +62,17 @@ type OnlyGetClone struct {
SinViewerPorFavor bool
}
type OnlyGetView struct {
SinClonerPorFavor bool
}
// Custom cloner func
func (ogv *OnlyGetView) Clone() *OnlyGetView {
return &OnlyGetView{
SinClonerPorFavor: ogv.SinClonerPorFavor,
}
}
type StructWithEmbedded struct {
A *StructWithPtrs
StructWithSlices

View File

@ -325,6 +325,58 @@ var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct {
Data []byte
}{})
// View returns a readonly view of OnlyGetView.
func (p *OnlyGetView) View() OnlyGetViewView {
return OnlyGetViewView{ж: p}
}
// OnlyGetViewView provides a read-only view over OnlyGetView.
//
// Its methods should only be called if `Valid()` returns true.
type OnlyGetViewView 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.
ж *OnlyGetView
}
// Valid reports whether underlying value is non-nil.
func (v OnlyGetViewView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
// the original.
func (v OnlyGetViewView) AsStruct() *OnlyGetView {
if v.ж == nil {
return nil
}
return v.ж.Clone()
}
func (v OnlyGetViewView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
func (v *OnlyGetViewView) UnmarshalJSON(b []byte) error {
if v.ж != nil {
return errors.New("already initialized")
}
if len(b) == 0 {
return nil
}
var x OnlyGetView
if err := json.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
func (v OnlyGetViewView) SinClonerPorFavor() bool { return v.ж.SinClonerPorFavor }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _OnlyGetViewViewNeedsRegeneration = OnlyGetView(struct {
SinClonerPorFavor bool
}{})
// View returns a readonly view of StructWithEmbedded.
func (p *StructWithEmbedded) View() StructWithEmbeddedView {
return StructWithEmbeddedView{ж: p}

View File

@ -40,7 +40,7 @@ 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}}{
func (v {{.ViewName}}) AsStruct() *{{.StructName}}{
if v.ж == nil {
return nil
}
@ -324,6 +324,7 @@ var (
flagCloneFunc = flag.Bool("clonefunc", false, "add a top-level Clone func")
flagCloneOnlyTypes = flag.String("clone-only-type", "", "comma-separated list of types (a subset of --type) that should only generate a go:generate clone line and not actual views")
flagViewOnlyTypes = flag.String("view-only-type", "", "comma-separated list of types (a subset of --type) that should only generate views and not be added to go:generate for cloner")
)
func main() {
@ -336,10 +337,21 @@ func main() {
}
typeNames := strings.Split(*flagTypes, ",")
viewOnlyTypes := map[string]bool{}
for _, t := range strings.Split(*flagViewOnlyTypes, ",") {
viewOnlyTypes[t] = true
}
var flagArgs []string
flagArgs = append(flagArgs, fmt.Sprintf("-clonefunc=%v", *flagCloneFunc))
if *flagTypes != "" {
flagArgs = append(flagArgs, "-type="+*flagTypes)
var cloneTypes []string
for _, t := range strings.Split(*flagTypes, ",") {
if _, ok := viewOnlyTypes[t]; !ok {
cloneTypes = append(cloneTypes, t)
}
}
flagArgs = append(flagArgs, "-type="+strings.Join(cloneTypes, ","))
}
if *flagBuildTags != "" {
flagArgs = append(flagArgs, "-tags="+*flagBuildTags)
@ -373,7 +385,7 @@ func main() {
break
}
}
if !hasClone {
if !hasClone && !viewOnlyTypes[typeName] {
runCloner = true
}
genView(buf, it, typ, pkg.Types)