blob: 970797a634cb21556689452c861efe3e20c9d85f [file] [log] [blame]
Brian Silvermanea2c95f2021-02-10 18:10:26 -08001#include "aos/aos_cli_utils.h"
2
3#include <sys/stat.h>
4#include <sys/types.h>
5#include <unistd.h>
6
7#include <iostream>
8
Austin Schuhc5fa6d92022-02-25 14:36:28 -08009DEFINE_string(config, "./aos_config.json", "File path of aos configuration");
Brian Silvermanea2c95f2021-02-10 18:10:26 -080010
11DEFINE_bool(
12 _bash_autocomplete, false,
13 "Internal use: Outputs channel list for use with autocomplete script.");
14DEFINE_string(_bash_autocomplete_word, "",
15 "Internal use: Current word being autocompleted");
16
17DEFINE_bool(all, false,
18 "If true, print out the channels for all nodes, not just the "
19 "channels which are visible on this node.");
20
21namespace aos {
22namespace {
23
24bool EndsWith(std::string_view str, std::string_view ending) {
25 const std::size_t offset = str.size() - ending.size();
26 return str.size() >= ending.size() &&
27 std::equal(str.begin() + offset, str.end(), ending.begin(),
28 ending.end());
29}
30
31} // namespace
32
33bool CliUtilInfo::Initialize(
34 int *argc, char ***argv,
Austin Schuh59f3b0f2021-07-31 20:50:40 -070035 std::function<bool(const aos::Channel *)> channel_filter,
36 bool expect_args) {
Brian Silvermanea2c95f2021-02-10 18:10:26 -080037 // Don't generate failure output if the config doesn't exist while attempting
38 // to autocomplete.
Milind Upadhyay17098ba2022-04-15 22:18:50 -070039 if (FLAGS__bash_autocomplete &&
40 (!(EndsWith(FLAGS_config, ".json") || EndsWith(FLAGS_config, ".bfbs")))) {
Brian Silvermanea2c95f2021-02-10 18:10:26 -080041 std::cout << "COMPREPLY=()";
42 return true;
43 }
44
Milind Upadhyay17098ba2022-04-15 22:18:50 -070045 config = aos::configuration::MaybeReadConfig(FLAGS_config);
46 if (FLAGS__bash_autocomplete && !config.has_value()) {
47 std::cout << "COMPREPLY=()";
48 return true;
49 }
50 CHECK(config.has_value()) << "Could not read config. See above errors.";
51
Brian Silvermanea2c95f2021-02-10 18:10:26 -080052 event_loop.emplace(&config->message());
53 event_loop->SkipTimingReport();
54 event_loop->SkipAosLog();
55
Austin Schuhd30a5ca2021-07-31 20:49:35 -070056 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
Brian Silvermanea2c95f2021-02-10 18:10:26 -080057 event_loop->configuration()->channels();
Austin Schuhd30a5ca2021-07-31 20:49:35 -070058
59 do {
60 std::string channel_name;
61 std::string message_type;
62 if (*argc > 1) {
63 channel_name = (*argv)[1];
64 ShiftArgs(argc, argv);
65 }
66 if (*argc > 1) {
67 message_type = (*argv)[1];
68 ShiftArgs(argc, argv);
69 }
70
71 if (FLAGS__bash_autocomplete) {
72 Autocomplete(channel_name, message_type, channel_filter);
73 return true;
74 }
75
76 if (channel_name.empty() && message_type.empty()) {
77 std::cout << "Channels:\n";
78 for (const aos::Channel *channel : *channels) {
79 if (FLAGS_all || channel_filter(channel)) {
80 std::cout << channel->name()->c_str() << ' '
81 << channel->type()->c_str() << '\n';
82 }
83 }
84 return true;
85 }
86
87 std::vector<const aos::Channel *> found_channels_now;
88 bool found_exact = false;
Brian Silvermanea2c95f2021-02-10 18:10:26 -080089 for (const aos::Channel *channel : *channels) {
Austin Schuhd30a5ca2021-07-31 20:49:35 -070090 if (!FLAGS_all && !channel_filter(channel)) {
91 continue;
Brian Silvermanea2c95f2021-02-10 18:10:26 -080092 }
Austin Schuhd30a5ca2021-07-31 20:49:35 -070093 if (channel->name()->c_str() != channel_name) {
94 continue;
Brian Silvermanea2c95f2021-02-10 18:10:26 -080095 }
Austin Schuhd30a5ca2021-07-31 20:49:35 -070096 if (channel->type()->string_view() == message_type) {
97 if (!found_exact) {
98 found_channels_now.clear();
99 found_exact = true;
100 }
101 } else if (!found_exact && channel->type()->string_view().find(
102 message_type) != std::string_view::npos) {
103 } else {
104 continue;
105 }
106 found_channels_now.push_back(channel);
Brian Silvermanea2c95f2021-02-10 18:10:26 -0800107 }
Brian Silvermanea2c95f2021-02-10 18:10:26 -0800108
Austin Schuhd30a5ca2021-07-31 20:49:35 -0700109 if (found_channels_now.empty()) {
110 LOG(FATAL)
111 << "Could not find any channels with the given name and type for "
112 << channel_name << " " << message_type;
113 } else if (found_channels_now.size() > 1 && !message_type.empty()) {
114 LOG(FATAL) << "Multiple channels found with same type for "
115 << channel_name << " " << message_type;
116 }
117 for (const aos::Channel *channel : found_channels_now) {
118 found_channels.push_back(channel);
119 }
Austin Schuh59f3b0f2021-07-31 20:50:40 -0700120 } while (expect_args && *argc > 1);
Brian Silvermanea2c95f2021-02-10 18:10:26 -0800121
122 return false;
123}
124
125void CliUtilInfo::Autocomplete(
126 std::string_view channel_name, std::string_view message_type,
127 std::function<bool(const aos::Channel *)> channel_filter) {
128 const aos::Configuration *const config_msg = event_loop->configuration();
129 const bool unique_match =
130 std::count_if(config_msg->channels()->begin(),
131 config_msg->channels()->end(),
132 [channel_name, message_type](const aos::Channel *channel) {
133 return channel->name()->string_view() == channel_name &&
134 channel->type()->string_view() == message_type;
135 }) == 1;
136
137 const bool editing_message =
138 !channel_name.empty() && FLAGS__bash_autocomplete_word == message_type;
139 const bool editing_channel =
140 !editing_message && FLAGS__bash_autocomplete_word == channel_name;
141
142 std::cout << "COMPREPLY=(";
143
144 // If we have a unique match, don't provide any suggestions. Otherwise, check
145 // that were're editing one of the two positional arguments.
146 if (!unique_match && (editing_message || editing_channel)) {
147 for (const aos::Channel *channel : *config_msg->channels()) {
148 if (FLAGS_all || channel_filter(channel)) {
149 // Suggest only message types if the message type argument is being
150 // entered.
151 if (editing_message) {
152 // Then, filter for only channel names that match exactly and types
153 // that begin with message_type.
154 if (channel->name()->string_view() == channel_name &&
155 channel->type()->string_view().find(message_type) == 0) {
156 std::cout << '\'' << channel->type()->c_str() << "' ";
157 }
158 } else if (channel->name()->string_view().find(channel_name) == 0) {
159 // If the message type empty, then return full autocomplete.
160 // Otherwise, since the message type is poulated yet not being edited,
161 // the user must be editing the channel name alone, in which case only
162 // suggest channel names, not pairs.
163 if (message_type.empty()) {
164 std::cout << '\'' << channel->name()->c_str() << ' '
165 << channel->type()->c_str() << "' ";
166 } else {
167 std::cout << '\'' << channel->name()->c_str() << "' ";
168 }
169 }
170 }
171 }
172 }
173 std::cout << ')';
174}
175
176} // namespace aos