Skip to content

Commit 127cf6e

Browse files
authored
refactor(server): group plans by type in server plans human output (#315)
1 parent e0fe8ae commit 127cf6e

5 files changed

Lines changed: 76 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
## Added
10+
### Added
1111

1212
- Add `gateway plans` command for listing gateway plans.
1313

14+
### Changed
15+
16+
- In all outputs of `server plans`, sort plans by CPU count, memory amount, and storage size.
17+
- In human readable output of `server plans`, group plans by type.
18+
1419
## [3.8.1] - 2024-05-24
1520

1621
### Changed

internal/commands/server/firewall/show_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ func TestFirewallShowHumanOutput(t *testing.T) {
143143
},
144144
}
145145

146-
expected := ` Firewall rules
146+
expected := `
147+
Firewall rules
147148
148149
# Action Source Destination Dir Proto
149150
─── ──────── ─────────────── ───────────── ───── ──────────

internal/commands/server/plan_list.go

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package server
22

33
import (
4+
"sort"
5+
"strings"
6+
47
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
58
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
9+
"github.com/UpCloudLtd/upcloud-go-api/v8/upcloud"
610
)
711

812
// PlanListCommand creates the "server plans" command
@@ -18,14 +22,28 @@ type planListCommand struct {
1822

1923
// ExecuteWithoutArguments implements commands.NoArgumentCommand
2024
func (s *planListCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
21-
plans, err := exec.All().GetPlans(exec.Context())
25+
plansObj, err := exec.All().GetPlans(exec.Context())
2226
if err != nil {
2327
return nil, err
2428
}
2529

26-
rows := []output.TableRow{}
27-
for _, p := range plans.Plans {
28-
rows = append(rows, output.TableRow{
30+
plans := plansObj.Plans
31+
sort.Slice(plans, func(i, j int) bool {
32+
if plans[i].CoreNumber != plans[j].CoreNumber {
33+
return plans[i].CoreNumber < plans[j].CoreNumber
34+
}
35+
36+
if plans[i].MemoryAmount != plans[j].MemoryAmount {
37+
return plans[i].MemoryAmount < plans[j].MemoryAmount
38+
}
39+
40+
return plans[i].StorageSize < plans[j].StorageSize
41+
})
42+
43+
rows := make(map[string][]output.TableRow)
44+
for _, p := range plans {
45+
key := planType(p)
46+
rows[key] = append(rows[key], output.TableRow{
2947
p.Name,
3048
p.CoreNumber,
3149
p.MemoryAmount,
@@ -37,7 +55,33 @@ func (s *planListCommand) ExecuteWithoutArguments(exec commands.Executor) (outpu
3755

3856
return output.MarshaledWithHumanOutput{
3957
Value: plans,
40-
Output: output.Table{
58+
Output: output.Combined{
59+
planSection("general_purpose", "General purpose", rows["general_purpose"]),
60+
planSection("high_cpu", "High CPU", rows["high_cpu"]),
61+
planSection("high_memory", "High memory", rows["high_memory"]),
62+
planSection("developer", "Developer", rows["developer"]),
63+
},
64+
}, nil
65+
}
66+
67+
func planType(p upcloud.Plan) string {
68+
if strings.HasPrefix(p.Name, "DEV-") {
69+
return "developer"
70+
}
71+
if strings.HasPrefix(p.Name, "HICPU-") {
72+
return "high_cpu"
73+
}
74+
if strings.HasPrefix(p.Name, "HIMEM-") {
75+
return "high_memory"
76+
}
77+
return "general_purpose"
78+
}
79+
80+
func planSection(key, title string, rows []output.TableRow) output.CombinedSection {
81+
return output.CombinedSection{
82+
Key: key,
83+
Title: title,
84+
Contents: output.Table{
4185
Columns: []output.TableColumn{
4286
{Key: "name", Header: "Name"},
4387
{Key: "cores", Header: "Cores"},
@@ -48,5 +92,5 @@ func (s *planListCommand) ExecuteWithoutArguments(exec commands.Executor) (outpu
4892
},
4993
Rows: rows,
5094
},
51-
}, nil
95+
}
5296
}

internal/output/combined.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,30 @@ func (m Combined) MarshalHuman() ([]byte, error) {
7575
marshaled = prefixLines(marshaled, " ")
7676
}
7777
out = append(out, marshaled...)
78+
79+
// ensure newline before first section
80+
if i == 0 && firstNonSpaceChar(out) != '\n' {
81+
out = append([]byte("\n"), out...)
82+
}
83+
84+
// dont add newline after the last section
7885
if i < len(m)-1 {
79-
// dont add newline after the last section
8086
out = append(out, []byte("\n")...)
8187
}
8288
}
8389

8490
return out, nil
8591
}
8692

93+
func firstNonSpaceChar(bytes []byte) byte {
94+
for _, b := range bytes {
95+
if b != ' ' {
96+
return b
97+
}
98+
}
99+
return 0
100+
}
101+
87102
func prefixLines(marshaled []byte, s string) (out []byte) {
88103
padding := []byte(s)
89104
for _, b := range marshaled {

internal/output/combined_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ func TestCombined(t *testing.T) {
6969
},
7070
}},
7171
},
72-
expectedHumanResult: ` MOCK
72+
expectedHumanResult: `
73+
MOCK
7374
7475
B D
7576
─── ────

0 commit comments

Comments
 (0)