mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
cmd/viewer: add support for map-like container types
This PR modifies viewTypeForContainerType to use the last type parameter of a container type as the value type, enabling the implementation of map-like container types where the second-to-last (usually first) type parameter serves as the key type. It also adds a MapContainer type to test the code generation. Updates #12736 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
parent
2f27319baf
commit
f8f9f05ffe
@ -152,13 +152,53 @@ func ContainerViewOf[T views.ViewCloner[T, V], V views.StructView[T]](c *Contain
|
|||||||
return ContainerView[T, V]{c}
|
return ContainerView[T, V]{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapContainer is a predefined map-like container type.
|
||||||
|
// Unlike [Container], it has two type parameters, where the value
|
||||||
|
// is the second parameter.
|
||||||
|
type MapContainer[K comparable, V views.Cloner[V]] struct {
|
||||||
|
Items map[K]V
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MapContainer[K, V]) Clone() *MapContainer[K, V] {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var m map[K]V
|
||||||
|
if c.Items != nil {
|
||||||
|
m = make(map[K]V, len(c.Items))
|
||||||
|
for i := range m {
|
||||||
|
m[i] = c.Items[i].Clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &MapContainer[K, V]{m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapContainerView is a pre-defined readonly view of a [MapContainer][K, T].
|
||||||
|
type MapContainerView[K comparable, T views.ViewCloner[T, V], V views.StructView[T]] 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.
|
||||||
|
ж *MapContainer[K, T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cv MapContainerView[K, T, V]) Items() views.MapFn[K, T, V] {
|
||||||
|
return views.MapFnOf(cv.ж.Items, func(t T) V { return t.View() })
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapContainerViewOf[K comparable, T views.ViewCloner[T, V], V views.StructView[T]](c *MapContainer[K, T]) MapContainerView[K, T, V] {
|
||||||
|
return MapContainerView[K, T, V]{c}
|
||||||
|
}
|
||||||
|
|
||||||
type GenericBasicStruct[T BasicType] struct {
|
type GenericBasicStruct[T BasicType] struct {
|
||||||
Value T
|
Value T
|
||||||
}
|
}
|
||||||
|
|
||||||
type StructWithContainers struct {
|
type StructWithContainers struct {
|
||||||
IntContainer Container[int]
|
IntContainer Container[int]
|
||||||
CloneableContainer Container[*StructWithPtrs]
|
CloneableContainer Container[*StructWithPtrs]
|
||||||
BasicGenericContainer Container[GenericBasicStruct[int]]
|
BasicGenericContainer Container[GenericBasicStruct[int]]
|
||||||
ClonableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
||||||
|
CloneableMap MapContainer[int, *StructWithPtrs]
|
||||||
|
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
|
||||||
}
|
}
|
||||||
|
@ -426,14 +426,18 @@ func (src *StructWithContainers) Clone() *StructWithContainers {
|
|||||||
dst := new(StructWithContainers)
|
dst := new(StructWithContainers)
|
||||||
*dst = *src
|
*dst = *src
|
||||||
dst.CloneableContainer = *src.CloneableContainer.Clone()
|
dst.CloneableContainer = *src.CloneableContainer.Clone()
|
||||||
dst.ClonableGenericContainer = *src.ClonableGenericContainer.Clone()
|
dst.CloneableGenericContainer = *src.CloneableGenericContainer.Clone()
|
||||||
|
dst.CloneableMap = *src.CloneableMap.Clone()
|
||||||
|
dst.CloneableGenericMap = *src.CloneableGenericMap.Clone()
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||||
var _StructWithContainersCloneNeedsRegeneration = StructWithContainers(struct {
|
var _StructWithContainersCloneNeedsRegeneration = StructWithContainers(struct {
|
||||||
IntContainer Container[int]
|
IntContainer Container[int]
|
||||||
CloneableContainer Container[*StructWithPtrs]
|
CloneableContainer Container[*StructWithPtrs]
|
||||||
BasicGenericContainer Container[GenericBasicStruct[int]]
|
BasicGenericContainer Container[GenericBasicStruct[int]]
|
||||||
ClonableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
||||||
|
CloneableMap MapContainer[int, *StructWithPtrs]
|
||||||
|
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
|
||||||
}{})
|
}{})
|
||||||
|
@ -657,14 +657,22 @@ func (v StructWithContainersView) CloneableContainer() ContainerView[*StructWith
|
|||||||
func (v StructWithContainersView) BasicGenericContainer() Container[GenericBasicStruct[int]] {
|
func (v StructWithContainersView) BasicGenericContainer() Container[GenericBasicStruct[int]] {
|
||||||
return v.ж.BasicGenericContainer
|
return v.ж.BasicGenericContainer
|
||||||
}
|
}
|
||||||
func (v StructWithContainersView) ClonableGenericContainer() ContainerView[*GenericNoPtrsStruct[int], GenericNoPtrsStructView[int]] {
|
func (v StructWithContainersView) CloneableGenericContainer() ContainerView[*GenericNoPtrsStruct[int], GenericNoPtrsStructView[int]] {
|
||||||
return ContainerViewOf(&v.ж.ClonableGenericContainer)
|
return ContainerViewOf(&v.ж.CloneableGenericContainer)
|
||||||
|
}
|
||||||
|
func (v StructWithContainersView) CloneableMap() MapContainerView[int, *StructWithPtrs, StructWithPtrsView] {
|
||||||
|
return MapContainerViewOf(&v.ж.CloneableMap)
|
||||||
|
}
|
||||||
|
func (v StructWithContainersView) CloneableGenericMap() MapContainerView[int, *GenericNoPtrsStruct[int], GenericNoPtrsStructView[int]] {
|
||||||
|
return MapContainerViewOf(&v.ж.CloneableGenericMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||||
var _StructWithContainersViewNeedsRegeneration = StructWithContainers(struct {
|
var _StructWithContainersViewNeedsRegeneration = StructWithContainers(struct {
|
||||||
IntContainer Container[int]
|
IntContainer Container[int]
|
||||||
CloneableContainer Container[*StructWithPtrs]
|
CloneableContainer Container[*StructWithPtrs]
|
||||||
BasicGenericContainer Container[GenericBasicStruct[int]]
|
BasicGenericContainer Container[GenericBasicStruct[int]]
|
||||||
ClonableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
|
||||||
|
CloneableMap MapContainer[int, *StructWithPtrs]
|
||||||
|
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
|
||||||
}{})
|
}{})
|
||||||
|
@ -448,7 +448,7 @@ func viewTypeForContainerType(typ types.Type) (*types.Named, *types.Func) {
|
|||||||
}
|
}
|
||||||
// ...and add the element view type.
|
// ...and add the element view type.
|
||||||
// For that, we need to first determine the named elem type...
|
// For that, we need to first determine the named elem type...
|
||||||
elemType, ok := baseType(containerType.TypeArgs().At(0)).(*types.Named)
|
elemType, ok := baseType(containerType.TypeArgs().At(containerType.TypeArgs().Len() - 1)).(*types.Named)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user