Pull PrintMessage out into aos_cli_utils.h

This lets us share aos_dump and log_cat printing code so they both have
the same feature set.

Change-Id: I66802f9e071fd072ee64bf317b0ff704132a250c
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/BUILD b/aos/BUILD
index 69303a9..d78b303 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -542,6 +542,7 @@
         ":configuration",
         "//aos:init",
         "//aos/events:shm_event_loop",
+        "//aos/events:simulated_event_loop",
         "@com_github_google_glog//:glog",
     ],
 )
diff --git a/aos/aos_cli_utils.cc b/aos/aos_cli_utils.cc
index dbd1bcd..2016a71 100644
--- a/aos/aos_cli_utils.cc
+++ b/aos/aos_cli_utils.cc
@@ -4,8 +4,14 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <iostream>
 
+#include "aos/configuration.h"
+#include "aos/events/shm_event_loop.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/time/time.h"
+
 DEFINE_string(config, "aos_config.json", "File path of aos configuration");
 
 DEFINE_bool(
@@ -21,6 +27,8 @@
 namespace aos {
 namespace {
 
+namespace chrono = std::chrono;
+
 bool EndsWith(std::string_view str, std::string_view ending) {
   const std::size_t offset = str.size() - ending.size();
   return str.size() >= ending.size() &&
@@ -28,6 +36,27 @@
                     ending.end());
 }
 
+void StreamSeconds(std::ostream &stream,
+                   const aos::monotonic_clock::time_point now) {
+  if (now < monotonic_clock::epoch()) {
+    chrono::seconds seconds =
+        chrono::duration_cast<chrono::seconds>(now.time_since_epoch());
+
+    stream << "-" << -seconds.count() << "." << std::setfill('0')
+           << std::setw(9)
+           << chrono::duration_cast<chrono::nanoseconds>(seconds -
+                                                         now.time_since_epoch())
+                  .count();
+  } else {
+    chrono::seconds seconds =
+        chrono::duration_cast<chrono::seconds>(now.time_since_epoch());
+    stream << seconds.count() << "." << std::setfill('0') << std::setw(9)
+           << chrono::duration_cast<chrono::nanoseconds>(
+                  now.time_since_epoch() - seconds)
+                  .count();
+  }
+}
+
 }  // namespace
 
 bool CliUtilInfo::Initialize(
@@ -179,4 +208,87 @@
   std::cout << ')';
 }
 
+void PrintMessage(const std::string_view node_name, const aos::Channel *channel,
+                  const aos::Context &context, aos::FastStringBuilder *builder,
+                  PrintOptions options) {
+  // Print the flatbuffer out to stdout, both to remove the
+  // unnecessary cruft from glog and to allow the user to readily
+  // redirect just the logged output independent of any debugging
+  // information on stderr.
+
+  builder->Reset();
+
+  CHECK(flatbuffers::Verify(*channel->schema(),
+                            *channel->schema()->root_table(),
+                            static_cast<const uint8_t *>(context.data),
+                            static_cast<size_t>(context.size)))
+      << ": Corrupted flatbuffer on " << channel->name()->c_str() << " "
+      << channel->type()->c_str();
+
+  aos::FlatbufferToJson(
+      builder, channel->schema(), static_cast<const uint8_t *>(context.data),
+      {options.pretty, static_cast<size_t>(options.max_vector_size),
+       options.pretty_max, options.use_hex});
+
+  if (options.json) {
+    std::cout << "{";
+    if (!node_name.empty()) {
+      std::cout << "\"node\": \"" << node_name << "\", ";
+    }
+    std::cout << "\"monotonic_event_time\": ";
+    StreamSeconds(std::cout, context.monotonic_event_time);
+    std::cout << ", \"realtime_event_time\": \"" << context.realtime_event_time
+              << "\", ";
+
+    if (context.monotonic_remote_time != context.monotonic_event_time) {
+      std::cout << "\"monotonic_remote_time\": ";
+      StreamSeconds(std::cout, context.monotonic_remote_time);
+      std::cout << ", \"realtime_remote_time\": \""
+                << context.realtime_remote_time << "\", ";
+    }
+
+    std::cout << "\"channel\": "
+              << aos::configuration::StrippedChannelToString(channel)
+              << ", \"data\": " << *builder << "}\n";
+  } else {
+    if (!node_name.empty()) {
+      std::cout << node_name << " ";
+    }
+
+    if (options.print_timestamps) {
+      if (context.monotonic_remote_time != context.monotonic_event_time) {
+        std::cout << context.realtime_event_time << " ("
+                  << context.monotonic_event_time << ") sent "
+                  << context.realtime_remote_time << " ("
+                  << context.monotonic_remote_time << ") "
+                  << channel->name()->c_str() << ' ' << channel->type()->c_str()
+                  << ": " << *builder << "\n";
+      } else {
+        std::cout << context.realtime_event_time << " ("
+                  << context.monotonic_event_time << ") "
+                  << channel->name()->c_str() << ' ' << channel->type()->c_str()
+                  << ": " << *builder << "\n";
+      }
+    } else {
+      std::cout << *builder << '\n';
+    }
+  }
+}
+
+void PrintMessage(const aos::Channel *channel, const aos::Context &context,
+                  aos::FastStringBuilder *builder, PrintOptions options) {
+  PrintMessage("", channel, context, builder, options);
+}
+
+void PrintMessage(const std::string_view node_name,
+                  aos::NodeEventLoopFactory *node_factory,
+                  const aos::Channel *channel, const aos::Context &context,
+                  aos::FastStringBuilder *builder, PrintOptions options) {
+  if (!options.json && options.distributed_clock) {
+    std::cout << node_factory->ToDistributedClock(context.monotonic_event_time)
+              << " ";
+  }
+  PrintMessage(node_name, channel, context, builder, options);
+}
+
 }  // namespace aos
diff --git a/aos/aos_cli_utils.h b/aos/aos_cli_utils.h
index a2d8c62..7e143dd 100644
--- a/aos/aos_cli_utils.h
+++ b/aos/aos_cli_utils.h
@@ -3,10 +3,40 @@
 
 #include "aos/configuration.h"
 #include "aos/events/shm_event_loop.h"
+#include "aos/events/simulated_event_loop.h"
 #include "gflags/gflags.h"
 
 namespace aos {
 
+struct PrintOptions {
+  // Format the JSON with the pretty option.
+  bool pretty;
+  // Max vector size to skip expanding.
+  size_t max_vector_size;
+  // Put everything on a separate line instead of keeping small messages
+  // together.
+  bool pretty_max;
+  // Print the timestamps.
+  bool print_timestamps;
+  // Make everything JSON compliant.
+  bool json;
+  // Print the distributed clock.
+  bool distributed_clock;
+  // Print numbers out in hex.
+  bool use_hex;
+};
+
+// Print the flatbuffer out to stdout, both to remove the unnecessary cruft from
+// glog and to allow the user to readily redirect just the logged output
+// independent of any debugging information on stderr.
+void PrintMessage(const std::string_view node_name,
+                  aos::NodeEventLoopFactory *node_factory,
+                  const aos::Channel *channel, const aos::Context &context,
+                  aos::FastStringBuilder *builder, PrintOptions options);
+
+void PrintMessage(const aos::Channel *channel, const aos::Context &context,
+                  aos::FastStringBuilder *builder, PrintOptions options);
+
 // The information needed by the main function of a CLI tool.
 struct CliUtilInfo {
   // If this returns true, main should return immediately with 0.
diff --git a/aos/aos_dump.cc b/aos/aos_dump.cc
index 86f4305..9f3d01f 100644
--- a/aos/aos_dump.cc
+++ b/aos/aos_dump.cc
@@ -8,8 +8,9 @@
 #include "aos/json_to_flatbuffer.h"
 #include "gflags/gflags.h"
 
-DEFINE_int32(max_vector_size, 100,
+DEFINE_int64(max_vector_size, 100,
              "If positive, vectors longer than this will not be printed");
+DEFINE_bool(json, false, "If true, print fully valid JSON");
 DEFINE_bool(fetch, false,
             "If true, fetch the current message on the channel first");
 DEFINE_bool(pretty, false,
@@ -28,46 +29,6 @@
              "exiting.  -1 means forever, 0 means don't wait.");
 DEFINE_bool(use_hex, false, "Are integers in the messages printed in hex notation.");
 
-namespace {
-
-void PrintMessage(const aos::Channel *channel, const aos::Context &context,
-                  aos::FastStringBuilder *builder) {
-  // Print the flatbuffer out to stdout, both to remove the
-  // unnecessary cruft from glog and to allow the user to readily
-  // redirect just the logged output independent of any debugging
-  // information on stderr.
-
-  builder->Reset();
-
-  CHECK(flatbuffers::Verify(*channel->schema(),
-                            *channel->schema()->root_table(),
-                            static_cast<const uint8_t *>(context.data),
-                            static_cast<size_t>(context.size)))
-      << ": Corrupted flatbuffer on " << channel->name()->c_str() << " "
-      << channel->type()->c_str();
-
-  aos::FlatbufferToJson(
-      builder, channel->schema(), static_cast<const uint8_t *>(context.data),
-      {FLAGS_pretty, static_cast<size_t>(FLAGS_max_vector_size),
-       FLAGS_pretty_max, FLAGS_use_hex});
-
-  if (FLAGS_print_timestamps) {
-    if (context.monotonic_remote_time != context.monotonic_event_time) {
-      std::cout << context.realtime_remote_time << " ("
-                << context.monotonic_remote_time << ") delivered "
-                << context.realtime_event_time << " ("
-                << context.monotonic_event_time << "): " << *builder << '\n';
-    } else {
-      std::cout << context.realtime_event_time << " ("
-                << context.monotonic_event_time << "): " << *builder << '\n';
-    }
-  } else {
-    std::cout << *builder << '\n';
-  }
-}
-
-}  // namespace
-
 int main(int argc, char **argv) {
   gflags::SetUsageMessage(
       "Prints messages from arbitrary channels as they are received given a "
@@ -94,12 +55,23 @@
 
   aos::monotonic_clock::time_point next_send_time =
       aos::monotonic_clock::min_time;
+
   for (const aos::Channel *channel : cli_info.found_channels) {
     if (FLAGS_fetch) {
       const std::unique_ptr<aos::RawFetcher> fetcher =
           cli_info.event_loop->MakeRawFetcher(channel);
       if (fetcher->Fetch()) {
-        PrintMessage(channel, fetcher->context(), &str_builder);
+        PrintMessage(
+            channel, fetcher->context(), &str_builder,
+            {
+                .pretty = FLAGS_pretty,
+                .max_vector_size = static_cast<size_t>(FLAGS_max_vector_size),
+                .pretty_max = FLAGS_pretty_max,
+                .print_timestamps = FLAGS_print_timestamps,
+                .json = FLAGS_json,
+                .distributed_clock = false,
+                .use_hex = FLAGS_use_hex,
+            });
         ++message_count;
       }
     }
@@ -120,7 +92,17 @@
             if (FLAGS_count > 0 && message_count >= FLAGS_count) {
               return;
             }
-            PrintMessage(channel, context, &str_builder);
+            PrintMessage(channel, context, &str_builder,
+                         {
+                             .pretty = FLAGS_pretty,
+                             .max_vector_size =
+                                 static_cast<size_t>(FLAGS_max_vector_size),
+                             .pretty_max = FLAGS_pretty_max,
+                             .print_timestamps = FLAGS_print_timestamps,
+                             .json = FLAGS_json,
+                             .distributed_clock = false,
+                             .use_hex = FLAGS_use_hex,
+                         });
             ++message_count;
             next_send_time = context.monotonic_event_time +
                              std::chrono::milliseconds(FLAGS_rate_limit);
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 0afdda0..e32da37 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -310,6 +310,7 @@
     visibility = ["//visibility:public"],
     deps = [
         ":log_reader",
+        "//aos:aos_cli_utils",
         "//aos:configuration",
         "//aos:init",
         "//aos:json_to_flatbuffer",
diff --git a/aos/events/logging/log_cat.cc b/aos/events/logging/log_cat.cc
index 1333de6..1f166e4 100644
--- a/aos/events/logging/log_cat.cc
+++ b/aos/events/logging/log_cat.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "absl/strings/escaping.h"
+#include "aos/aos_cli_utils.h"
 #include "aos/configuration.h"
 #include "aos/events/logging/log_reader.h"
 #include "aos/events/simulated_event_loop.h"
@@ -33,10 +34,14 @@
 DEFINE_bool(format_raw, true,
             "If true and --raw is specified, print out raw data, but use the "
             "schema to format the data.");
-DEFINE_int32(max_vector_size, 100,
+DEFINE_int64(max_vector_size, 100,
              "If positive, vectors longer than this will not be printed");
 DEFINE_bool(pretty, false,
             "If true, pretty print the messages on multiple lines");
+DEFINE_bool(
+    pretty_max, false,
+    "If true, expand every field to its own line (expands more than -pretty)");
+DEFINE_bool(print_timestamps, true, "If true, timestamps are printed.");
 DEFINE_bool(print, true,
             "If true, actually print the messages.  If false, discard them, "
             "confirming they can be parsed.");
@@ -59,94 +64,6 @@
 using aos::monotonic_clock;
 namespace chrono = std::chrono;
 
-void StreamSeconds(std::ostream &stream,
-                   const aos::monotonic_clock::time_point now) {
-  if (now < monotonic_clock::epoch()) {
-    chrono::seconds seconds =
-        chrono::duration_cast<chrono::seconds>(now.time_since_epoch());
-
-    stream << "-" << -seconds.count() << "." << std::setfill('0')
-           << std::setw(9)
-           << chrono::duration_cast<chrono::nanoseconds>(seconds -
-                                                         now.time_since_epoch())
-                  .count();
-  } else {
-    chrono::seconds seconds =
-        chrono::duration_cast<chrono::seconds>(now.time_since_epoch());
-    stream << seconds.count() << "." << std::setfill('0') << std::setw(9)
-           << chrono::duration_cast<chrono::nanoseconds>(
-                  now.time_since_epoch() - seconds)
-                  .count();
-  }
-}
-
-// Print the flatbuffer out to stdout, both to remove the unnecessary cruft from
-// glog and to allow the user to readily redirect just the logged output
-// independent of any debugging information on stderr.
-void PrintMessage(const std::string_view node_name,
-                  aos::NodeEventLoopFactory *node_factory,
-                  const aos::Channel *channel, const aos::Context &context,
-                  aos::FastStringBuilder *builder) {
-  builder->Reset();
-  CHECK(flatbuffers::Verify(*channel->schema(),
-                            *channel->schema()->root_table(),
-                            static_cast<const uint8_t *>(context.data),
-                            static_cast<size_t>(context.size)))
-      << ": Corrupted flatbuffer on " << channel->name()->c_str() << " "
-      << channel->type()->c_str();
-
-  aos::FlatbufferToJson(
-      builder, channel->schema(), static_cast<const uint8_t *>(context.data),
-      {.multi_line = FLAGS_pretty,
-       .max_vector_size = static_cast<size_t>(FLAGS_max_vector_size),
-       .max_multi_line = false,
-       .use_hex = FLAGS_use_hex});
-
-  if (FLAGS_json) {
-    std::cout << "{";
-    if (!node_name.empty()) {
-      std::cout << "\"node\": \"" << node_name << "\", ";
-    }
-    std::cout << "\"monotonic_event_time\": ";
-    StreamSeconds(std::cout, context.monotonic_event_time);
-    std::cout << ", \"realtime_event_time\": \"" << context.realtime_event_time
-              << "\", ";
-
-    if (context.monotonic_remote_time != context.monotonic_event_time) {
-      std::cout << "\"monotonic_remote_time\": ";
-      StreamSeconds(std::cout, context.monotonic_remote_time);
-      std::cout << ", \"realtime_remote_time\": \""
-                << context.realtime_remote_time << "\", ";
-    }
-
-    std::cout << "\"channel\": "
-              << aos::configuration::StrippedChannelToString(channel)
-              << ", \"data\": " << *builder << "}" << std::endl;
-  } else {
-    if (FLAGS_distributed_clock) {
-      std::cout << node_factory->ToDistributedClock(
-                       context.monotonic_event_time)
-                << " ";
-    }
-    if (!node_name.empty()) {
-      std::cout << node_name << " ";
-    }
-    if (context.monotonic_remote_time != context.monotonic_event_time) {
-      std::cout << context.realtime_event_time << " ("
-                << context.monotonic_event_time << ") sent "
-                << context.realtime_remote_time << " ("
-                << context.monotonic_remote_time << ") "
-                << channel->name()->c_str() << ' ' << channel->type()->c_str()
-                << ": " << *builder << std::endl;
-    } else {
-      std::cout << context.realtime_event_time << " ("
-                << context.monotonic_event_time << ") "
-                << channel->name()->c_str() << ' ' << channel->type()->c_str()
-                << ": " << *builder << std::endl;
-    }
-  }
-}
-
 // Prints out raw log parts to stdout.
 int PrintRaw(int argc, char **argv) {
   if (argc != 2) {
@@ -336,7 +253,17 @@
             return;
           }
 
-          PrintMessage(node_name_, node_factory_, channel, context, builder_);
+          PrintMessage(
+              node_name_, node_factory_, channel, context, builder_,
+              {
+                  .pretty = FLAGS_pretty,
+                  .max_vector_size = static_cast<size_t>(FLAGS_max_vector_size),
+                  .pretty_max = FLAGS_pretty_max,
+                  .print_timestamps = FLAGS_print_timestamps,
+                  .json = FLAGS_json,
+                  .distributed_clock = FLAGS_distributed_clock,
+                  .use_hex = FLAGS_use_hex,
+              });
           ++(*message_print_counter_);
           if (FLAGS_count > 0 && *message_print_counter_ >= FLAGS_count) {
             factory_->Exit();