Skip to content

Commit

Permalink
add 'orderedmap' data structure
Browse files Browse the repository at this point in the history
  • Loading branch information
alessio-perugini committed Oct 10, 2023
1 parent 94413ed commit d657217
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 43 deletions.
2 changes: 1 addition & 1 deletion internal/cli/core/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (ir coreListResult) String() string {
t.SetHeader(tr("ID"), tr("Installed"), tr("Latest"), tr("Name"))
for _, platform := range ir.platforms {
name := ""
if installed := platform.GetLatestRelease(); installed != nil {
if installed := platform.GetInstalledRelease(); installed != nil {
name = installed.Name
}
if name == "" {
Expand Down
61 changes: 19 additions & 42 deletions internal/cli/feedback/result/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,33 @@
package result

import (
"slices"

"github.com/arduino/arduino-cli/internal/orderedmap"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/iancoleman/orderedmap"
semver "go.bug.st/relaxed-semver"
)

// NewPlatformResult creates a new result.Platform from rpc.PlatformSummary
func NewPlatformResult(in *rpc.PlatformSummary) *Platform {
meta := in.Metadata
res := &Platform{
Id: meta.Id,
Maintainer: meta.Maintainer,
Website: meta.Website,
Email: meta.Email,
ManuallyInstalled: meta.ManuallyInstalled,
Deprecated: meta.Deprecated,
Indexed: meta.Indexed,

Releases: orderedmap.New(),
InstalledVersion: in.InstalledVersion,
LatestVersion: in.LatestVersion,
}

releases := orderedmap.New[string, *PlatformRelease]()
for k, v := range in.Releases {
res.Releases.Set(k, NewPlatformReleaseResult(v))
releases.Set(k, NewPlatformReleaseResult(v))
}
res.Releases.SortKeys(func(keys []string) {
slices.SortFunc(keys, func(x, y string) int {
return semver.ParseRelaxed(x).CompareTo(semver.ParseRelaxed(y))
})
releases.SortKeys(func(x, y string) int {
return semver.ParseRelaxed(x).CompareTo(semver.ParseRelaxed(y))
})

versions := []*semver.RelaxedVersion{}
for version := range in.Releases {
versions = append(versions, semver.ParseRelaxed(version))
return &Platform{
Id: in.Metadata.Id,
Maintainer: in.Metadata.Maintainer,
Website: in.Metadata.Website,
Email: in.Metadata.Email,
ManuallyInstalled: in.Metadata.ManuallyInstalled,
Deprecated: in.Metadata.Deprecated,
Indexed: in.Metadata.Indexed,
Releases: releases,
InstalledVersion: in.InstalledVersion,
LatestVersion: in.LatestVersion,
}
slices.SortFunc(versions, (*semver.RelaxedVersion).CompareTo)
for _, version := range versions {
res.Releases.Set(version.String(), NewPlatformReleaseResult(in.Releases[version.String()]))
}
return res
}

// Platform maps a rpc.Platform
Expand All @@ -70,28 +55,20 @@ type Platform struct {
Deprecated bool `json:"deprecated,omitempty"`
Indexed bool `json:"indexed,omitempty"`

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

InstalledVersion string `json:"installed_version,omitempty"`
LatestVersion string `json:"latest_version,omitempty"`
}

// GetLatestRelease returns the latest relase of this platform or nil if none available.
func (p *Platform) GetLatestRelease() *PlatformRelease {
res, ok := p.Releases.Get(p.LatestVersion)
if !ok {
return nil
}
return res.(*PlatformRelease)
return p.Releases.Get(p.LatestVersion)
}

// GetInstalledRelease returns the installed relase of this platform or nil if none available.
func (p *Platform) GetInstalledRelease() *PlatformRelease {
res, ok := p.Releases.Get(p.InstalledVersion)
if !ok {
return nil
}
return res.(*PlatformRelease)
return p.Releases.Get(p.InstalledVersion)
}

// NewPlatformReleaseResult creates a new result.PlatformRelease from rpc.PlatformRelease
Expand Down
150 changes: 150 additions & 0 deletions internal/orderedmap/orderedmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
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
}

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

// Get retrieves the value corresponding to key
func (m *Map[K, V]) Get(key K) V {
return m.kv[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]
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]
return has
}

// MarshalJSON marshal the map into json mantaining the order of the key
func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
if m.Size() == 0 {
return []byte("{}"), nil
}
var buf bytes.Buffer
buf.WriteByte('{')
encoder := json.NewEncoder(&buf)
for _, k := range m.o {
if err := encoder.Encode(k); err != nil {
return nil, err
}
buf.WriteByte(':')
if err := encoder.Encode(m.kv[k]); err != nil {
return nil, err
}
buf.WriteByte(',')
}
buf.Truncate(buf.Len() - 1) // remove last `,`
buf.WriteByte('}')
return buf.Bytes(), nil
}

// 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 {
m.Remove(key)
}
m.kv[key] = value
m.o = append(m.o, key)
}

// Size returns the number of elements in the map
func (m *Map[K, V]) 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)
for i, k := range m.o {
if k == key {
m.o = append(m.o[:i], m.o[i+1:]...)
return
}
}
}

// 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] {
for _, source := range sources {
for _, key := range source.o {
value := source.kv[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 {
keys := make([]K, len(m.o))
copy(keys, m.o)
return keys
}

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

func (m *Map[K, V]) 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 {
values := make([]V, len(m.o))
for i, key := range m.o {
values[i] = m.kv[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]()
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 d657217

Please sign in to comment.