Skip to content

Commit

Permalink
Made orderedmap more generic
Browse files Browse the repository at this point in the history
  • Loading branch information
cmaglie committed Oct 11, 2023
1 parent d657217 commit da5821f
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 66 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ require (
github.com/djherbis/nio/v3 v3.0.1
github.com/fatih/color v1.7.0
github.com/gofrs/uuid/v5 v5.0.0
github.com/iancoleman/orderedmap v0.3.0
github.com/leonelquinteros/gotext v1.4.0
github.com/mailru/easyjson v0.7.7
github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
Expand Down
18 changes: 8 additions & 10 deletions internal/cli/feedback/result/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@ import (

// NewPlatformResult creates a new result.Platform from rpc.PlatformSummary
func NewPlatformResult(in *rpc.PlatformSummary) *Platform {
releases := orderedmap.New[string, *PlatformRelease]()
releases := orderedmap.NewWithConversionFunc[*semver.Version, *PlatformRelease, string]((*semver.Version).String)
for k, v := range in.Releases {
releases.Set(k, NewPlatformReleaseResult(v))
releases.Set(semver.MustParse(k), NewPlatformReleaseResult(v))
}
releases.SortKeys(func(x, y string) int {
return semver.ParseRelaxed(x).CompareTo(semver.ParseRelaxed(y))
})
releases.SortKeys((*semver.Version).CompareTo)

return &Platform{
Id: in.Metadata.Id,
Expand All @@ -40,8 +38,8 @@ func NewPlatformResult(in *rpc.PlatformSummary) *Platform {
Deprecated: in.Metadata.Deprecated,
Indexed: in.Metadata.Indexed,
Releases: releases,
InstalledVersion: in.InstalledVersion,
LatestVersion: in.LatestVersion,
InstalledVersion: semver.MustParse(in.InstalledVersion),
LatestVersion: semver.MustParse(in.LatestVersion),
}
}

Expand All @@ -55,10 +53,10 @@ type Platform struct {
Deprecated bool `json:"deprecated,omitempty"`
Indexed bool `json:"indexed,omitempty"`

Releases *orderedmap.Map[string, *PlatformRelease] `json:"releases,omitempty"`
Releases orderedmap.Map[*semver.Version, *PlatformRelease] `json:"releases,omitempty"`

InstalledVersion string `json:"installed_version,omitempty"`
LatestVersion string `json:"latest_version,omitempty"`
InstalledVersion *semver.Version `json:"installed_version,omitempty"`
LatestVersion *semver.Version `json:"latest_version,omitempty"`
}

// GetLatestRelease returns the latest relase of this platform or nil if none available.
Expand Down
111 changes: 58 additions & 53 deletions internal/orderedmap/orderedmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,65 @@ package orderedmap
import (
"bytes"
"encoding/json"
"reflect"
"slices"
)

// Map is a container of properties
type Map[K comparable, V any] struct {
kv map[K]V
o []K
// Map is a map that keeps ordering insertion.
type Map[K any, V any] interface {
Get(K) V
Set(K, V)
Keys() []K
Merge(...Map[K, V]) Map[K, V]
SortKeys(f func(x, y K) int)
SortStableKeys(f func(x, y K) int)
}

// NewWithConversionFunc creates a map using the given conversion function
// to convert non-comparable key type to comparable items.
// The conversion function must be bijective.
func NewWithConversionFunc[K any, V any, C comparable](conv func(K) C) Map[K, V] {
return &mapImpl[K, V, C]{
conv: conv,
kv: map[C]V{},
o: []K{},
}
}

// New returns a new Map
func New[K comparable, V any]() *Map[K, V] {
return &Map[K, V]{
kv: map[K]V{},
o: []K{},
// New creates a map
func New[K comparable, V any]() *mapImpl[K, V, K] {

Check warning on line 31 in internal/orderedmap/orderedmap.go

View workflow job for this annotation

GitHub Actions / check-style (./)

unexported-return: exported func New returns unexported type *orderedmap.mapImpl[K, V, K], which can be annoying to use (revive)
return &mapImpl[K, V, K]{
conv: func(in K) K { return in }, // identity
kv: map[K]V{},
o: []K{},
}
}

type mapImpl[K any, V any, C comparable] struct {
conv func(K) C
kv map[C]V
o []K
}

// Get retrieves the value corresponding to key
func (m *Map[K, V]) Get(key K) V {
return m.kv[key]
func (m *mapImpl[K, V, C]) Get(key K) V {
return m.kv[m.conv(key)]
}

// GetOk retrieves the value corresponding to key and returns a true/false indicator
// to check if the key is present in the map (true if the key is present)
func (m *Map[K, V]) GetOk(key K) (V, bool) {
v, ok := m.kv[key]
func (m *mapImpl[K, V, C]) GetOk(key K) (V, bool) {
v, ok := m.kv[m.conv(key)]
return v, ok
}

// ContainsKey returns true if the map contains the specified key
func (m *Map[K, V]) ContainsKey(key K) bool {
_, has := m.kv[key]
func (m *mapImpl[K, V, C]) ContainsKey(key K) bool {
_, has := m.kv[m.conv(key)]
return has
}

// MarshalJSON marshal the map into json mantaining the order of the key
func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
func (m *mapImpl[K, V, C]) MarshalJSON() ([]byte, error) {
if m.Size() == 0 {
return []byte("{}"), nil
}
Expand All @@ -52,7 +73,7 @@ func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
return nil, err
}
buf.WriteByte(':')
if err := encoder.Encode(m.kv[k]); err != nil {
if err := encoder.Encode(m.kv[m.conv(k)]); err != nil {
return nil, err
}
buf.WriteByte(',')
Expand All @@ -63,24 +84,26 @@ func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
}

// Set inserts or replaces an existing key-value pair in the map
func (m *Map[K, V]) Set(key K, value V) {
if _, has := m.kv[key]; has {
func (m *mapImpl[K, V, C]) Set(key K, value V) {
compKey := m.conv(key)
if _, has := m.kv[compKey]; has {
m.Remove(key)
}
m.kv[key] = value
m.kv[compKey] = value
m.o = append(m.o, key)
}

// Size returns the number of elements in the map
func (m *Map[K, V]) Size() int {
func (m *mapImpl[K, V, C]) Size() int {
return len(m.kv)
}

// Remove removes the key from the map
func (m *Map[K, V]) Remove(key K) {
delete(m.kv, key)
func (m *mapImpl[K, V, C]) Remove(key K) {
compKey := m.conv(key)
delete(m.kv, compKey)
for i, k := range m.o {
if k == key {
if m.conv(k) == compKey {
m.o = append(m.o[:i], m.o[i+1:]...)
return
}
Expand All @@ -89,62 +112,44 @@ func (m *Map[K, V]) Remove(key K) {

// Merge merges other Maps into this one. Each key/value of the merged Maps replaces
// the key/value present in the original Map.
func (m *Map[K, V]) Merge(sources ...*Map[K, V]) *Map[K, V] {
func (m *mapImpl[K, V, C]) Merge(sources ...Map[K, V]) Map[K, V] {
for _, source := range sources {
for _, key := range source.o {
value := source.kv[key]
for _, key := range source.Keys() {
value := source.Get(key)
m.Set(key, value)
}
}
return m
}

// Keys returns an array of the keys contained in the Map
func (m *Map[K, V]) Keys() []K {
func (m *mapImpl[K, V, C]) Keys() []K {
keys := make([]K, len(m.o))
copy(keys, m.o)
return keys
}

func (m *Map[K, V]) SortKeys(f func(x, y K) int) {
func (m *mapImpl[K, V, C]) SortKeys(f func(x, y K) int) {
slices.SortFunc(m.o, f)
}

func (m *Map[K, V]) SortStableKeys(f func(x, y K) int) {
func (m *mapImpl[K, V, C]) SortStableKeys(f func(x, y K) int) {
slices.SortStableFunc(m.o, f)
}

// Values returns an array of the values contained in the Map. Duplicated
// values are repeated in the list accordingly.
func (m *Map[K, V]) Values() []V {
func (m *mapImpl[K, V, C]) Values() []V {
values := make([]V, len(m.o))
for i, key := range m.o {
values[i] = m.kv[key]
values[i] = m.kv[m.conv(key)]
}
return values
}

// AsMap returns the underlying map[string]string. This is useful if you need to
// for ... range but without the requirement of the ordered elements.
func (m *Map[K, V]) AsMap() map[K]V {
return m.kv
}

// Clone makes a copy of the Map
func (m *Map[K, V]) Clone() *Map[K, V] {
clone := New[K, V]()
func (m *mapImpl[K, V, C]) Clone() Map[K, V] {
clone := NewWithConversionFunc[K, V, C](m.conv)
clone.Merge(m)
return clone
}

// Equals returns true if the current Map contains the same key/value pairs of
// the Map passed as argument, the order of insertion does not matter.
func (m *Map[K, V]) Equals(other *Map[K, V]) bool {
return reflect.DeepEqual(m.kv, other.kv)
}

// EqualsWithOrder returns true if the current Map contains the same key/value pairs of
// the Map passed as argument with the same order of insertion.
func (m *Map[K, V]) EqualsWithOrder(other *Map[K, V]) bool {
return reflect.DeepEqual(m.o, other.o) && reflect.DeepEqual(m.kv, other.kv)
}

0 comments on commit da5821f

Please sign in to comment.