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:
Nick Khyl 2024-08-14 15:06:50 -05:00 committed by Nick Khyl
parent 2f27319baf
commit f8f9f05ffe
4 changed files with 68 additions and 16 deletions

View File

@ -152,6 +152,44 @@ func ContainerViewOf[T views.ViewCloner[T, V], V views.StructView[T]](c *Contain
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 {
Value T
}
@ -160,5 +198,7 @@ type StructWithContainers struct {
IntContainer Container[int]
CloneableContainer Container[*StructWithPtrs]
BasicGenericContainer Container[GenericBasicStruct[int]]
ClonableGenericContainer Container[*GenericNoPtrsStruct[int]]
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
CloneableMap MapContainer[int, *StructWithPtrs]
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
}

View File

@ -426,7 +426,9 @@ func (src *StructWithContainers) Clone() *StructWithContainers {
dst := new(StructWithContainers)
*dst = *src
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
}
@ -435,5 +437,7 @@ func (src *StructWithContainers) Clone() *StructWithContainers {
IntContainer Container[int]
CloneableContainer Container[*StructWithPtrs]
BasicGenericContainer Container[GenericBasicStruct[int]]
ClonableGenericContainer Container[*GenericNoPtrsStruct[int]]
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
CloneableMap MapContainer[int, *StructWithPtrs]
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
}{})

View File

@ -657,8 +657,14 @@ func (v StructWithContainersView) CloneableContainer() ContainerView[*StructWith
func (v StructWithContainersView) BasicGenericContainer() Container[GenericBasicStruct[int]] {
return v.ж.BasicGenericContainer
}
func (v StructWithContainersView) ClonableGenericContainer() ContainerView[*GenericNoPtrsStruct[int], GenericNoPtrsStructView[int]] {
return ContainerViewOf(&v.ж.ClonableGenericContainer)
func (v StructWithContainersView) CloneableGenericContainer() ContainerView[*GenericNoPtrsStruct[int], GenericNoPtrsStructView[int]] {
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.
@ -666,5 +672,7 @@ func (v StructWithContainersView) ClonableGenericContainer() ContainerView[*Gene
IntContainer Container[int]
CloneableContainer Container[*StructWithPtrs]
BasicGenericContainer Container[GenericBasicStruct[int]]
ClonableGenericContainer Container[*GenericNoPtrsStruct[int]]
CloneableGenericContainer Container[*GenericNoPtrsStruct[int]]
CloneableMap MapContainer[int, *StructWithPtrs]
CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]]
}{})

View File

@ -448,7 +448,7 @@ func viewTypeForContainerType(typ types.Type) (*types.Named, *types.Func) {
}
// ...and add the element view 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 {
return nil, nil
}