Skip to content

Commit edb0511

Browse files
committed
Refactor selection menu display and prompting
The new helpers will be able to be reused for instance prompting. This version separates the prompting for a selection label from the display and looking up the group associated with that label. It also adds prompts to the user to make it clearer what label is expected, and catches different error cases separately. One slight downgrade for this version is it removes the functionality to type in a group name directly to select it. However, that functionality was not clearly advertised and unlikely to be faster than typing a numeric label except in cases that seem unlikely for the majority of users. Bug: 511316553
1 parent a8b20d7 commit edb0511

1 file changed

Lines changed: 63 additions & 49 deletions

File tree

  • base/cvd/cuttlefish/host/commands/cvd/cli/selector

base/cvd/cuttlefish/host/commands/cvd/cli/selector/selector.cpp

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <iostream>
2020
#include <memory>
21+
#include <ostream>
2122
#include <sstream>
2223
#include <string>
2324
#include <utility>
@@ -34,6 +35,11 @@ namespace cuttlefish {
3435
namespace selector {
3536
namespace {
3637

38+
enum class DisplayBehavior {
39+
LabelGroup,
40+
LabelInstance,
41+
};
42+
3743
Result<InstanceDatabase::Filter> BuildFilterFromSelectors(
3844
const SelectorOptions& selectors) {
3945
InstanceDatabase::Filter filter;
@@ -49,67 +55,75 @@ Result<InstanceDatabase::Filter> BuildFilterFromSelectors(
4955
return filter;
5056
}
5157

52-
std::string SelectionMenu(const std::vector<LocalInstanceGroup>& groups) {
53-
// Multiple instance groups found, please choose one:
54-
// [i] : group_name (created: TIME)
55-
// <a> instance0.device_name() (id: instance_id)
56-
// <b> instance1.device_name() (id: instance_id)
57-
std::stringstream ss;
58-
ss << "Multiple instance groups found, please choose one:" << std::endl;
59-
int group_idx = 0;
60-
for (const auto& group : groups) {
61-
fmt::print(ss, " [{}] : {} (created: {})\n", group_idx, group.GroupName(),
58+
std::string GroupDisplay(const std::vector<LocalInstanceGroup>& groups,
59+
const DisplayBehavior behavior) {
60+
std::stringstream result;
61+
int group_index = 0;
62+
for (const LocalInstanceGroup& group : groups) {
63+
if (behavior == DisplayBehavior::LabelGroup) {
64+
fmt::print(result, "[{}] - ", group_index);
65+
}
66+
fmt::print(result, "{} (created: {})\n", group.GroupName(),
6267
Format(group.StartTime()));
63-
for (const auto& instance : group.Instances()) {
64-
fmt::print(ss, " {}-{} (id : {})\n", group.GroupName(),
68+
69+
int instance_index = 0;
70+
for (const LocalInstance& instance : group.Instances()) {
71+
result << "\t";
72+
if (behavior == DisplayBehavior::LabelInstance) {
73+
fmt::print(result, "[{}] - ", instance_index);
74+
}
75+
fmt::print(result, "{}-{} (id : {})\n", group.GroupName(),
6576
instance.Name(), instance.Id());
77+
78+
instance_index++;
6679
}
67-
group_idx++;
80+
81+
group_index++;
6882
}
69-
return ss.str();
83+
return result.str();
7084
}
7185

72-
Result<LocalInstanceGroup> PromptUserForGroup(
73-
const InstanceManager& instance_manager, const CommandRequest& request,
74-
InstanceDatabase::Filter filter) {
75-
// show the menu and let the user choose
76-
std::vector<LocalInstanceGroup> groups =
77-
CF_EXPECT(instance_manager.FindGroups({}));
78-
std::string menu = SelectionMenu(groups);
79-
80-
std::cout << menu << "\n";
81-
std::unique_ptr<InterruptibleTerminal> terminal_ =
86+
Result<int> PromptForSelection(const int max_selection) {
87+
std::unique_ptr<InterruptibleTerminal> terminal =
8288
std::make_unique<InterruptibleTerminal>();
8389

8490
TerminalColors colors(isatty(2));
85-
while (true) {
86-
std::string input_line = CF_EXPECT(terminal_->ReadLine());
87-
int selection = -1;
88-
std::string chosen_group_name;
89-
if (absl::SimpleAtoi(input_line, &selection)) {
90-
const int n_groups = groups.size();
91-
if (n_groups <= selection || selection < 0) {
92-
fmt::print(std::cerr,
93-
"\n Selection {}{}{} is beyond the range {}[0, {}]{}\n\n",
94-
colors.BoldRed(), selection, colors.Reset(), colors.Cyan(),
95-
n_groups - 1, colors.Reset());
96-
continue;
97-
}
98-
chosen_group_name = groups[selection].GroupName();
99-
} else {
100-
chosen_group_name = std::string(absl::StripAsciiWhitespace(input_line));
101-
}
10291

103-
filter.group_name = chosen_group_name;
104-
Result<LocalInstanceGroup> instance_group_result =
105-
instance_manager.FindGroup(filter);
106-
if (instance_group_result.ok()) {
107-
return instance_group_result;
92+
int selection = -1;
93+
while (selection < 0 || selection > max_selection) {
94+
fmt::print(std::cout, "\nSelect {}[0,{}]{}: ", colors.Cyan(), max_selection,
95+
colors.Reset());
96+
std::cout << std::flush;
97+
std::string input_line = CF_EXPECT(terminal->ReadLine());
98+
if (!absl::SimpleAtoi(input_line, &selection)) {
99+
selection = -1;
100+
fmt::print(std::cerr, "Selection \"{}{}{}\" is not a valid.\n",
101+
colors.BoldRed(), input_line, colors.Reset());
102+
continue;
103+
}
104+
if (selection > max_selection) {
105+
fmt::print(std::cerr,
106+
"Selection \"{}{}{}\" is beyond the allowed range.\n",
107+
colors.BoldRed(), selection, colors.Reset());
108+
continue;
108109
}
109-
fmt::print(std::cerr,
110-
"\n Failed to find a group whose name is {}\"{}\"{}\n\n",
111-
colors.BoldRed(), chosen_group_name, colors.Reset());
112110
}
111+
return selection;
112+
}
113+
114+
Result<LocalInstanceGroup> PromptUserForGroup(
115+
const InstanceManager& instance_manager, const CommandRequest& request,
116+
InstanceDatabase::Filter filter) {
117+
const std::vector<LocalInstanceGroup> groups =
118+
CF_EXPECT(instance_manager.FindGroups({}));
119+
std::cout << GroupDisplay(groups, DisplayBehavior::LabelGroup);
120+
121+
const int selection = CF_EXPECT(PromptForSelection(groups.size() - 1));
122+
auto group_filter = InstanceDatabase::Filter{
123+
.group_name = groups[selection].GroupName(),
124+
};
125+
126+
return CF_EXPECT(instance_manager.FindGroup(group_filter));
113127
}
114128

115129
Result<std::pair<LocalInstance, LocalInstanceGroup>> PromptUserForInstance(

0 commit comments

Comments
 (0)