54 std::string format =
"json";
58 for (
size_t i = 0; i < arg_vec.size(); ++i) {
59 const std::string& token = arg_vec[i];
60 if (token ==
"--format") {
61 if (i + 1 >= arg_vec.size()) {
62 return absl::InvalidArgumentError(
"--format requires a value.");
64 format = absl::AsciiStrToLower(arg_vec[++i]);
65 }
else if (absl::StartsWith(token,
"--format=")) {
66 format = absl::AsciiStrToLower(token.substr(9));
67 }
else if (token ==
"--range") {
68 if (i + 1 >= arg_vec.size()) {
69 return absl::InvalidArgumentError(
"--range requires a value (start-end).");
71 std::string range = arg_vec[++i];
72 size_t dash_pos = range.find(
'-');
73 if (dash_pos == std::string::npos) {
74 return absl::InvalidArgumentError(
"--range format must be start-end (e.g. 0-100)");
76 if (!absl::SimpleAtoi(range.substr(0, dash_pos), &start_id) ||
77 !absl::SimpleAtoi(range.substr(dash_pos + 1), &end_id)) {
78 return absl::InvalidArgumentError(
"Invalid range format");
80 }
else if (absl::StartsWith(token,
"--range=")) {
81 std::string range = token.substr(8);
82 size_t dash_pos = range.find(
'-');
83 if (dash_pos == std::string::npos) {
84 return absl::InvalidArgumentError(
"--range format must be start-end (e.g. 0-100)");
86 if (!absl::SimpleAtoi(range.substr(0, dash_pos), &start_id) ||
87 !absl::SimpleAtoi(range.substr(dash_pos + 1), &end_id)) {
88 return absl::InvalidArgumentError(
"Invalid range format");
93 if (format !=
"json" && format !=
"text") {
94 return absl::InvalidArgumentError(
"--format must be either json or text");
99 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
102 auto rom_or = LoadRomFromFlag();
104 return rom_or.status();
106 rom_storage = std::move(rom_or.value());
110 auto messages = LoadMessages(rom);
113 end_id =
static_cast<int>(messages.size()) - 1;
116 start_id = std::max(0, std::min(start_id,
static_cast<int>(messages.size()) - 1));
117 end_id = std::max(start_id, std::min(end_id,
static_cast<int>(messages.size()) - 1));
119 if (format ==
"json") {
121 std::cout << absl::StrFormat(
" \"total_messages\": %zu,\n", messages.size());
122 std::cout << absl::StrFormat(
" \"range\": [%d, %d],\n", start_id, end_id);
123 std::cout <<
" \"messages\": [\n";
126 for (
int i = start_id; i <= end_id; ++i) {
127 const auto& msg = messages[i];
128 if (!first) std::cout <<
",\n";
130 std::cout << absl::StrFormat(
" \"id\": %d,\n", msg.ID);
131 std::cout << absl::StrFormat(
" \"address\": \"0x%06X\",\n", msg.Address);
134 std::string escaped_text = msg.ContentsParsed;
136 while ((pos = escaped_text.find(
'"', pos)) != std::string::npos) {
137 escaped_text.insert(pos,
"\\");
140 std::cout << absl::StrFormat(
" \"text\": \"%s\"\n", escaped_text);
144 std::cout <<
"\n ]\n";
147 std::cout << absl::StrFormat(
"📝 Messages %d-%d (Total: %zu)\n",
148 start_id, end_id, messages.size());
149 std::cout << std::string(60,
'=') <<
"\n";
150 for (
int i = start_id; i <= end_id; ++i) {
151 const auto& msg = messages[i];
152 std::cout << absl::StrFormat(
"[%03d] @ 0x%06X\n", msg.ID, msg.Address);
153 std::cout <<
" " << msg.ContentsParsed <<
"\n";
154 std::cout << std::string(60,
'-') <<
"\n";
158 return absl::OkStatus();
164 std::string format =
"json";
166 for (
size_t i = 0; i < arg_vec.size(); ++i) {
167 const std::string& token = arg_vec[i];
168 if (token ==
"--id") {
169 if (i + 1 >= arg_vec.size()) {
170 return absl::InvalidArgumentError(
"--id requires a value.");
172 if (!absl::SimpleAtoi(arg_vec[++i], &message_id)) {
173 return absl::InvalidArgumentError(
"Invalid message ID format.");
175 }
else if (absl::StartsWith(token,
"--id=")) {
176 if (!absl::SimpleAtoi(token.substr(5), &message_id)) {
177 return absl::InvalidArgumentError(
"Invalid message ID format.");
179 }
else if (token ==
"--format") {
180 if (i + 1 >= arg_vec.size()) {
181 return absl::InvalidArgumentError(
"--format requires a value.");
183 format = absl::AsciiStrToLower(arg_vec[++i]);
184 }
else if (absl::StartsWith(token,
"--format=")) {
185 format = absl::AsciiStrToLower(token.substr(9));
189 if (message_id < 0) {
190 return absl::InvalidArgumentError(
191 "Usage: message-read --id <message_id> [--format <json|text>]");
194 if (format !=
"json" && format !=
"text") {
195 return absl::InvalidArgumentError(
"--format must be either json or text");
200 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
203 auto rom_or = LoadRomFromFlag();
205 return rom_or.status();
207 rom_storage = std::move(rom_or.value());
211 auto messages = LoadMessages(rom);
213 if (message_id >=
static_cast<int>(messages.size())) {
214 return absl::NotFoundError(
215 absl::StrFormat(
"Message ID %d not found (max: %d)",
216 message_id, messages.size() - 1));
219 const auto& msg = messages[message_id];
221 if (format ==
"json") {
223 std::cout << absl::StrFormat(
" \"id\": %d,\n", msg.ID);
224 std::cout << absl::StrFormat(
" \"address\": \"0x%06X\",\n", msg.Address);
227 std::string escaped_text = msg.ContentsParsed;
229 while ((pos = escaped_text.find(
'"', pos)) != std::string::npos) {
230 escaped_text.insert(pos,
"\\");
233 std::cout << absl::StrFormat(
" \"text\": \"%s\",\n", escaped_text);
234 std::cout << absl::StrFormat(
" \"length\": %zu\n", msg.Data.size());
237 std::cout << absl::StrFormat(
"📝 Message #%d\n", msg.ID);
238 std::cout << absl::StrFormat(
"Address: 0x%06X\n", msg.Address);
239 std::cout << absl::StrFormat(
"Length: %zu bytes\n", msg.Data.size());
240 std::cout << std::string(60,
'-') <<
"\n";
241 std::cout << msg.ContentsParsed <<
"\n";
244 return absl::OkStatus();
250 std::string format =
"json";
252 for (
size_t i = 0; i < arg_vec.size(); ++i) {
253 const std::string& token = arg_vec[i];
254 if (token ==
"--query") {
255 if (i + 1 >= arg_vec.size()) {
256 return absl::InvalidArgumentError(
"--query requires a value.");
258 query = arg_vec[++i];
259 }
else if (absl::StartsWith(token,
"--query=")) {
260 query = token.substr(8);
261 }
else if (token ==
"--format") {
262 if (i + 1 >= arg_vec.size()) {
263 return absl::InvalidArgumentError(
"--format requires a value.");
265 format = absl::AsciiStrToLower(arg_vec[++i]);
266 }
else if (absl::StartsWith(token,
"--format=")) {
267 format = absl::AsciiStrToLower(token.substr(9));
272 return absl::InvalidArgumentError(
273 "Usage: message-search --query <text> [--format <json|text>]");
276 if (format !=
"json" && format !=
"text") {
277 return absl::InvalidArgumentError(
"--format must be either json or text");
282 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
285 auto rom_or = LoadRomFromFlag();
287 return rom_or.status();
289 rom_storage = std::move(rom_or.value());
293 auto messages = LoadMessages(rom);
294 std::string lowered_query = absl::AsciiStrToLower(query);
296 std::vector<int> matches;
297 for (
const auto& msg : messages) {
298 std::string lowered_text = absl::AsciiStrToLower(msg.ContentsParsed);
299 if (lowered_text.find(lowered_query) != std::string::npos) {
300 matches.push_back(msg.ID);
304 if (format ==
"json") {
306 std::cout << absl::StrFormat(
" \"query\": \"%s\",\n", query);
307 std::cout << absl::StrFormat(
" \"match_count\": %zu,\n", matches.size());
308 std::cout <<
" \"matches\": [\n";
310 for (
size_t i = 0; i < matches.size(); ++i) {
311 const auto& msg = messages[matches[i]];
312 if (i > 0) std::cout <<
",\n";
314 std::string escaped_text = msg.ContentsParsed;
316 while ((pos = escaped_text.find(
'"', pos)) != std::string::npos) {
317 escaped_text.insert(pos,
"\\");
322 std::cout << absl::StrFormat(
" \"id\": %d,\n", msg.ID);
323 std::cout << absl::StrFormat(
" \"address\": \"0x%06X\",\n", msg.Address);
324 std::cout << absl::StrFormat(
" \"text\": \"%s\"\n", escaped_text);
327 std::cout <<
"\n ]\n";
330 std::cout << absl::StrFormat(
"🔍 Search: \"%s\" → %zu match(es)\n",
331 query, matches.size());
332 std::cout << std::string(60,
'=') <<
"\n";
334 for (
int match_id : matches) {
335 const auto& msg = messages[match_id];
336 std::cout << absl::StrFormat(
"[%03d] @ 0x%06X\n", msg.ID, msg.Address);
337 std::cout <<
" " << msg.ContentsParsed <<
"\n";
338 std::cout << std::string(60,
'-') <<
"\n";
342 return absl::OkStatus();
347 std::string format =
"json";
349 for (
size_t i = 0; i < arg_vec.size(); ++i) {
350 const std::string& token = arg_vec[i];
351 if (token ==
"--format") {
352 if (i + 1 >= arg_vec.size()) {
353 return absl::InvalidArgumentError(
"--format requires a value.");
355 format = absl::AsciiStrToLower(arg_vec[++i]);
356 }
else if (absl::StartsWith(token,
"--format=")) {
357 format = absl::AsciiStrToLower(token.substr(9));
361 if (format !=
"json" && format !=
"text") {
362 return absl::InvalidArgumentError(
"--format must be either json or text");
367 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
370 auto rom_or = LoadRomFromFlag();
372 return rom_or.status();
374 rom_storage = std::move(rom_or.value());
378 auto messages = LoadMessages(rom);
380 size_t total_bytes = 0;
381 size_t max_length = 0;
382 size_t min_length = SIZE_MAX;
384 for (
const auto& msg : messages) {
385 size_t len = msg.Data.size();
387 max_length = std::max(max_length, len);
388 min_length = std::min(min_length, len);
391 double avg_length = messages.empty() ? 0.0 :
392 static_cast<double>(total_bytes) / messages.size();
394 if (format ==
"json") {
396 std::cout << absl::StrFormat(
" \"total_messages\": %zu,\n", messages.size());
397 std::cout << absl::StrFormat(
" \"total_bytes\": %zu,\n", total_bytes);
398 std::cout << absl::StrFormat(
" \"average_length\": %.2f,\n", avg_length);
399 std::cout << absl::StrFormat(
" \"min_length\": %zu,\n", min_length);
400 std::cout << absl::StrFormat(
" \"max_length\": %zu\n", max_length);
403 std::cout <<
"📊 Message Statistics\n";
404 std::cout << std::string(40,
'=') <<
"\n";
405 std::cout << absl::StrFormat(
"Total Messages: %zu\n", messages.size());
406 std::cout << absl::StrFormat(
"Total Bytes: %zu\n", total_bytes);
407 std::cout << absl::StrFormat(
"Average Length: %.2f bytes\n", avg_length);
408 std::cout << absl::StrFormat(
"Min Length: %zu bytes\n", min_length);
409 std::cout << absl::StrFormat(
"Max Length: %zu bytes\n", max_length);
412 return absl::OkStatus();