mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-24 09:39:39 +00:00
cmd/viewer, types/views, util/codegen: add viewer support for custom container types
This adds support for container-like types such as Container[T] that don't explicitly specify a view type for T. Instead, a package implementing a container type should also implement and export a ContainerView[T, V] type and a ContainerViewOf(*Container[T]) ContainerView[T, V] function, which returns a view for the specified container, inferring the element view type V from the element type T. Updates #12736 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"go4.org/mem"
|
||||
@@ -111,6 +112,13 @@ type StructView[T any] interface {
|
||||
AsStruct() T
|
||||
}
|
||||
|
||||
// Cloner is any type that has a Clone function returning a deep-clone of the receiver.
|
||||
type Cloner[T any] interface {
|
||||
// Clone returns a deep-clone of the receiver.
|
||||
// It returns nil, when the receiver is nil.
|
||||
Clone() T
|
||||
}
|
||||
|
||||
// ViewCloner is any type that has had View and Clone funcs generated using
|
||||
// tailscale.com/cmd/viewer.
|
||||
type ViewCloner[T any, V StructView[T]] interface {
|
||||
@@ -555,3 +563,46 @@ func (m MapFn[K, T, V]) Range(f MapRangeFn[K, V]) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ContainsPointers reports whether T contains any pointers,
|
||||
// either explicitly or implicitly.
|
||||
// It has special handling for some types that contain pointers
|
||||
// that we know are free from memory aliasing/mutation concerns.
|
||||
func ContainsPointers[T any]() bool {
|
||||
return containsPointers(reflect.TypeFor[T]())
|
||||
}
|
||||
|
||||
func containsPointers(typ reflect.Type) bool {
|
||||
switch typ.Kind() {
|
||||
case reflect.Pointer, reflect.UnsafePointer:
|
||||
return true
|
||||
case reflect.Chan, reflect.Map, reflect.Slice:
|
||||
return true
|
||||
case reflect.Array:
|
||||
return containsPointers(typ.Elem())
|
||||
case reflect.Interface, reflect.Func:
|
||||
return true // err on the safe side.
|
||||
case reflect.Struct:
|
||||
if isWellKnownImmutableStruct(typ) {
|
||||
return false
|
||||
}
|
||||
for i := range typ.NumField() {
|
||||
if containsPointers(typ.Field(i).Type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWellKnownImmutableStruct(typ reflect.Type) bool {
|
||||
switch typ.String() {
|
||||
case "time.Time":
|
||||
// time.Time contains a pointer that does not need copying
|
||||
return true
|
||||
case "netip.Addr", "netip.Prefix", "netip.AddrPort":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user