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 {
3435namespace selector {
3536namespace {
3637
38+ enum class DisplayBehavior {
39+ LabelGroup,
40+ LabelInstance,
41+ };
42+
3743Result<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, " \n Select {}[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
115129Result<std::pair<LocalInstance, LocalInstanceGroup>> PromptUserForInstance (
0 commit comments