summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKristoffer Dalby <kristoffer@tailscale.com>2024-01-23 13:23:23 +0100
committerKristoffer Dalby <kristoffer@tailscale.com>2024-01-23 13:46:02 +0100
commitbbd65862d7747af0b2f24bab1633efadadc2d3a2 (patch)
tree59e2e7d3e35774cfff0a6213d092c572fc5928e2
parent0e2cb76abe1867736fe2aea89d3bc5bbdb5911dd (diff)
downloadtailscale-kradalby/view-only-type.tar.xz
tailscale-kradalby/view-only-type.zip
cmd/viewer: add view-only optionkradalby/view-only-type
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>
-rw-r--r--cmd/viewer/tests/tests.go13
-rw-r--r--cmd/viewer/tests/tests_view.go52
-rw-r--r--cmd/viewer/viewer.go18
3 files changed, 79 insertions, 4 deletions
diff --git a/cmd/viewer/tests/tests.go b/cmd/viewer/tests/tests.go
index 55413403b..b5dd83f61 100644
--- a/cmd/viewer/tests/tests.go
+++ b/cmd/viewer/tests/tests.go
@@ -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
diff --git a/cmd/viewer/tests/tests_view.go b/cmd/viewer/tests/tests_view.go
index 0c6c9e287..685daf309 100644
--- a/cmd/viewer/tests/tests_view.go
+++ b/cmd/viewer/tests/tests_view.go
@@ -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}
diff --git a/cmd/viewer/viewer.go b/cmd/viewer/viewer.go
index 56b5e2bd2..0b66a13ea 100644
--- a/cmd/viewer/viewer.go
+++ b/cmd/viewer/viewer.go
@@ -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)