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,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]]
} }

View File

@ -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]]
}{}) }{})

View File

@ -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]]
}{}) }{})

View File

@ -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
} }