55 std::string format =
"json";
59 for (
size_t i = 0; i < arg_vec.size(); ++i) {
60 const std::string& token = arg_vec[i];
61 if (token ==
"--format") {
62 if (i + 1 >= arg_vec.size()) {
63 return absl::InvalidArgumentError(
"--format requires a value.");
65 format = absl::AsciiStrToLower(arg_vec[++i]);
66 }
else if (absl::StartsWith(token,
"--format=")) {
67 format = absl::AsciiStrToLower(token.substr(9));
68 }
else if (token ==
"--range") {
69 if (i + 1 >= arg_vec.size()) {
70 return absl::InvalidArgumentError(
71 "--range requires a value (start-end).");
73 std::string range = arg_vec[++i];
74 size_t dash_pos = range.find(
'-');
75 if (dash_pos == std::string::npos) {
76 return absl::InvalidArgumentError(
77 "--range format must be start-end (e.g. 0-100)");
79 if (!absl::SimpleAtoi(range.substr(0, dash_pos), &start_id) ||
80 !absl::SimpleAtoi(range.substr(dash_pos + 1), &end_id)) {
81 return absl::InvalidArgumentError(
"Invalid range format");
83 }
else if (absl::StartsWith(token,
"--range=")) {
84 std::string range = token.substr(8);
85 size_t dash_pos = range.find(
'-');
86 if (dash_pos == std::string::npos) {
87 return absl::InvalidArgumentError(
88 "--range format must be start-end (e.g. 0-100)");
90 if (!absl::SimpleAtoi(range.substr(0, dash_pos), &start_id) ||
91 !absl::SimpleAtoi(range.substr(dash_pos + 1), &end_id)) {
92 return absl::InvalidArgumentError(
"Invalid range format");
97 if (format !=
"json" && format !=
"text") {
98 return absl::InvalidArgumentError(
"--format must be either json or text");
103 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
106 auto rom_or = LoadRomFromFlag();
108 return rom_or.status();
110 rom_storage = std::move(rom_or.value());
114 auto messages = LoadMessages(rom);
117 end_id =
static_cast<int>(messages.size()) - 1;
121 std::max(0, std::min(start_id,
static_cast<int>(messages.size()) - 1));
122 end_id = std::max(start_id,
123 std::min(end_id,
static_cast<int>(messages.size()) - 1));
125 if (format ==
"json") {
127 std::cout << absl::StrFormat(
" \"total_messages\": %zu,\n",
129 std::cout << absl::StrFormat(
" \"range\": [%d, %d],\n", start_id, end_id);
130 std::cout <<
" \"messages\": [\n";
133 for (
int i = start_id; i <= end_id; ++i) {
134 const auto& msg = messages[i];
138 std::cout << absl::StrFormat(
" \"id\": %d,\n", msg.ID);
139 std::cout << absl::StrFormat(
" \"address\": \"0x%06X\",\n",
143 std::string escaped_text = msg.ContentsParsed;
145 while ((pos = escaped_text.find(
'"', pos)) != std::string::npos) {
146 escaped_text.insert(pos,
"\\");
149 std::cout << absl::StrFormat(
" \"text\": \"%s\"\n", escaped_text);
153 std::cout <<
"\n ]\n";
156 std::cout << absl::StrFormat(
"📝 Messages %d-%d (Total: %zu)\n", start_id,
157 end_id, messages.size());
158 std::cout << std::string(60,
'=') <<
"\n";
159 for (
int i = start_id; i <= end_id; ++i) {
160 const auto& msg = messages[i];
161 std::cout << absl::StrFormat(
"[%03d] @ 0x%06X\n", msg.ID, msg.Address);
162 std::cout <<
" " << msg.ContentsParsed <<
"\n";
163 std::cout << std::string(60,
'-') <<
"\n";
167 return absl::OkStatus();
173 std::string format =
"json";
175 for (
size_t i = 0; i < arg_vec.size(); ++i) {
176 const std::string& token = arg_vec[i];
177 if (token ==
"--id") {
178 if (i + 1 >= arg_vec.size()) {
179 return absl::InvalidArgumentError(
"--id requires a value.");
181 if (!absl::SimpleAtoi(arg_vec[++i], &message_id)) {
182 return absl::InvalidArgumentError(
"Invalid message ID format.");
184 }
else if (absl::StartsWith(token,
"--id=")) {
185 if (!absl::SimpleAtoi(token.substr(5), &message_id)) {
186 return absl::InvalidArgumentError(
"Invalid message ID format.");
188 }
else if (token ==
"--format") {
189 if (i + 1 >= arg_vec.size()) {
190 return absl::InvalidArgumentError(
"--format requires a value.");
192 format = absl::AsciiStrToLower(arg_vec[++i]);
193 }
else if (absl::StartsWith(token,
"--format=")) {
194 format = absl::AsciiStrToLower(token.substr(9));
198 if (message_id < 0) {
199 return absl::InvalidArgumentError(
200 "Usage: message-read --id <message_id> [--format <json|text>]");
203 if (format !=
"json" && format !=
"text") {
204 return absl::InvalidArgumentError(
"--format must be either json or text");
209 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
212 auto rom_or = LoadRomFromFlag();
214 return rom_or.status();
216 rom_storage = std::move(rom_or.value());
220 auto messages = LoadMessages(rom);
222 if (message_id >=
static_cast<int>(messages.size())) {
223 return absl::NotFoundError(absl::StrFormat(
224 "Message ID %d not found (max: %d)", message_id, messages.size() - 1));
227 const auto& msg = messages[message_id];
229 if (format ==
"json") {
231 std::cout << absl::StrFormat(
" \"id\": %d,\n", msg.ID);
232 std::cout << absl::StrFormat(
" \"address\": \"0x%06X\",\n", msg.Address);
235 std::string escaped_text = msg.ContentsParsed;
237 while ((pos = escaped_text.find(
'"', pos)) != std::string::npos) {
238 escaped_text.insert(pos,
"\\");
241 std::cout << absl::StrFormat(
" \"text\": \"%s\",\n", escaped_text);
242 std::cout << absl::StrFormat(
" \"length\": %zu\n", msg.Data.size());
245 std::cout << absl::StrFormat(
"📝 Message #%d\n", msg.ID);
246 std::cout << absl::StrFormat(
"Address: 0x%06X\n", msg.Address);
247 std::cout << absl::StrFormat(
"Length: %zu bytes\n", msg.Data.size());
248 std::cout << std::string(60,
'-') <<
"\n";
249 std::cout << msg.ContentsParsed <<
"\n";
252 return absl::OkStatus();
258 std::string format =
"json";
260 for (
size_t i = 0; i < arg_vec.size(); ++i) {
261 const std::string& token = arg_vec[i];
262 if (token ==
"--query") {
263 if (i + 1 >= arg_vec.size()) {
264 return absl::InvalidArgumentError(
"--query requires a value.");
266 query = arg_vec[++i];
267 }
else if (absl::StartsWith(token,
"--query=")) {
268 query = token.substr(8);
269 }
else if (token ==
"--format") {
270 if (i + 1 >= arg_vec.size()) {
271 return absl::InvalidArgumentError(
"--format requires a value.");
273 format = absl::AsciiStrToLower(arg_vec[++i]);
274 }
else if (absl::StartsWith(token,
"--format=")) {
275 format = absl::AsciiStrToLower(token.substr(9));
280 return absl::InvalidArgumentError(
281 "Usage: message-search --query <text> [--format <json|text>]");
284 if (format !=
"json" && format !=
"text") {
285 return absl::InvalidArgumentError(
"--format must be either json or text");
290 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
293 auto rom_or = LoadRomFromFlag();
295 return rom_or.status();
297 rom_storage = std::move(rom_or.value());
301 auto messages = LoadMessages(rom);
302 std::string lowered_query = absl::AsciiStrToLower(query);
304 std::vector<int> matches;
305 for (
const auto& msg : messages) {
306 std::string lowered_text = absl::AsciiStrToLower(msg.ContentsParsed);
307 if (lowered_text.find(lowered_query) != std::string::npos) {
308 matches.push_back(msg.ID);
312 if (format ==
"json") {
314 std::cout << absl::StrFormat(
" \"query\": \"%s\",\n", query);
315 std::cout << absl::StrFormat(
" \"match_count\": %zu,\n", matches.size());
316 std::cout <<
" \"matches\": [\n";
318 for (
size_t i = 0; i < matches.size(); ++i) {
319 const auto& msg = messages[matches[i]];
323 std::string escaped_text = msg.ContentsParsed;
325 while ((pos = escaped_text.find(
'"', pos)) != std::string::npos) {
326 escaped_text.insert(pos,
"\\");
331 std::cout << absl::StrFormat(
" \"id\": %d,\n", msg.ID);
332 std::cout << absl::StrFormat(
" \"address\": \"0x%06X\",\n",
334 std::cout << absl::StrFormat(
" \"text\": \"%s\"\n", escaped_text);
337 std::cout <<
"\n ]\n";
340 std::cout << absl::StrFormat(
"🔍 Search: \"%s\" → %zu match(es)\n", query,
342 std::cout << std::string(60,
'=') <<
"\n";
344 for (
int match_id : matches) {
345 const auto& msg = messages[match_id];
346 std::cout << absl::StrFormat(
"[%03d] @ 0x%06X\n", msg.ID, msg.Address);
347 std::cout <<
" " << msg.ContentsParsed <<
"\n";
348 std::cout << std::string(60,
'-') <<
"\n";
352 return absl::OkStatus();
357 std::string format =
"json";
359 for (
size_t i = 0; i < arg_vec.size(); ++i) {
360 const std::string& token = arg_vec[i];
361 if (token ==
"--format") {
362 if (i + 1 >= arg_vec.size()) {
363 return absl::InvalidArgumentError(
"--format requires a value.");
365 format = absl::AsciiStrToLower(arg_vec[++i]);
366 }
else if (absl::StartsWith(token,
"--format=")) {
367 format = absl::AsciiStrToLower(token.substr(9));
371 if (format !=
"json" && format !=
"text") {
372 return absl::InvalidArgumentError(
"--format must be either json or text");
377 if (rom_context !=
nullptr && rom_context->
is_loaded()) {
380 auto rom_or = LoadRomFromFlag();
382 return rom_or.status();
384 rom_storage = std::move(rom_or.value());
388 auto messages = LoadMessages(rom);
390 size_t total_bytes = 0;
391 size_t max_length = 0;
392 size_t min_length = SIZE_MAX;
394 for (
const auto& msg : messages) {
395 size_t len = msg.Data.size();
397 max_length = std::max(max_length, len);
398 min_length = std::min(min_length, len);
401 double avg_length = messages.empty()
403 :
static_cast<double>(total_bytes) / messages.size();
405 if (format ==
"json") {
407 std::cout << absl::StrFormat(
" \"total_messages\": %zu,\n",
409 std::cout << absl::StrFormat(
" \"total_bytes\": %zu,\n", total_bytes);
410 std::cout << absl::StrFormat(
" \"average_length\": %.2f,\n", avg_length);
411 std::cout << absl::StrFormat(
" \"min_length\": %zu,\n", min_length);
412 std::cout << absl::StrFormat(
" \"max_length\": %zu\n", max_length);
415 std::cout <<
"📊 Message Statistics\n";
416 std::cout << std::string(40,
'=') <<
"\n";
417 std::cout << absl::StrFormat(
"Total Messages: %zu\n", messages.size());
418 std::cout << absl::StrFormat(
"Total Bytes: %zu\n", total_bytes);
419 std::cout << absl::StrFormat(
"Average Length: %.2f bytes\n", avg_length);
420 std::cout << absl::StrFormat(
"Min Length: %zu bytes\n", min_length);
421 std::cout << absl::StrFormat(
"Max Length: %zu bytes\n", max_length);
424 return absl::OkStatus();