From f8f9f05ffe5e39e8f8e826915e3bb579d1ca2b12 Mon Sep 17 00:00:00 2001 From: Nick Khyl Date: Wed, 14 Aug 2024 15:06:50 -0500 Subject: [PATCH] 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 --- cmd/viewer/tests/tests.go | 48 ++++++++++++++++++++++++++++++--- cmd/viewer/tests/tests_clone.go | 14 ++++++---- cmd/viewer/tests/tests_view.go | 20 +++++++++----- cmd/viewer/viewer.go | 2 +- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/cmd/viewer/tests/tests.go b/cmd/viewer/tests/tests.go index 17bc1edc3..1f1ec0557 100644 --- a/cmd/viewer/tests/tests.go +++ b/cmd/viewer/tests/tests.go @@ -152,13 +152,53 @@ 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 } type StructWithContainers struct { - IntContainer Container[int] - CloneableContainer Container[*StructWithPtrs] - BasicGenericContainer Container[GenericBasicStruct[int]] - ClonableGenericContainer Container[*GenericNoPtrsStruct[int]] + IntContainer Container[int] + CloneableContainer Container[*StructWithPtrs] + BasicGenericContainer Container[GenericBasicStruct[int]] + CloneableGenericContainer Container[*GenericNoPtrsStruct[int]] + CloneableMap MapContainer[int, *StructWithPtrs] + CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]] } diff --git a/cmd/viewer/tests/tests_clone.go b/cmd/viewer/tests/tests_clone.go index b4d92d3ec..53e6bacfb 100644 --- a/cmd/viewer/tests/tests_clone.go +++ b/cmd/viewer/tests/tests_clone.go @@ -426,14 +426,18 @@ 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 } // A compilation failure here means this code must be regenerated, with the command at the top of this file. var _StructWithContainersCloneNeedsRegeneration = StructWithContainers(struct { - IntContainer Container[int] - CloneableContainer Container[*StructWithPtrs] - BasicGenericContainer Container[GenericBasicStruct[int]] - ClonableGenericContainer Container[*GenericNoPtrsStruct[int]] + IntContainer Container[int] + CloneableContainer Container[*StructWithPtrs] + BasicGenericContainer Container[GenericBasicStruct[int]] + CloneableGenericContainer Container[*GenericNoPtrsStruct[int]] + CloneableMap MapContainer[int, *StructWithPtrs] + CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]] }{}) diff --git a/cmd/viewer/tests/tests_view.go b/cmd/viewer/tests/tests_view.go index 44618e79e..cf07dc663 100644 --- a/cmd/viewer/tests/tests_view.go +++ b/cmd/viewer/tests/tests_view.go @@ -657,14 +657,22 @@ 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. var _StructWithContainersViewNeedsRegeneration = StructWithContainers(struct { - IntContainer Container[int] - CloneableContainer Container[*StructWithPtrs] - BasicGenericContainer Container[GenericBasicStruct[int]] - ClonableGenericContainer Container[*GenericNoPtrsStruct[int]] + IntContainer Container[int] + CloneableContainer Container[*StructWithPtrs] + BasicGenericContainer Container[GenericBasicStruct[int]] + CloneableGenericContainer Container[*GenericNoPtrsStruct[int]] + CloneableMap MapContainer[int, *StructWithPtrs] + CloneableGenericMap MapContainer[int, *GenericNoPtrsStruct[int]] }{}) diff --git a/cmd/viewer/viewer.go b/cmd/viewer/viewer.go index d77875ba8..2e122a128 100644 --- a/cmd/viewer/viewer.go +++ b/cmd/viewer/viewer.go @@ -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 }