Factor out JsonOptions into a struct

Change-Id: I2441069e86ea2eb95d8e1952be7c2a7a43036c74
diff --git a/aos/aos_dump.cc b/aos/aos_dump.cc
index 67afa7f..55ca57e 100644
--- a/aos/aos_dump.cc
+++ b/aos/aos_dump.cc
@@ -28,7 +28,7 @@
               << aos::FlatbufferToJson(
                      channel->schema(),
                      static_cast<const uint8_t *>(context.data),
-                     FLAGS_max_vector_size)
+                     {false, static_cast<size_t>(FLAGS_max_vector_size)})
               << '\n';
   } else {
     std::cout << context.realtime_event_time << " ("
@@ -36,7 +36,7 @@
               << aos::FlatbufferToJson(
                      channel->schema(),
                      static_cast<const uint8_t *>(context.data),
-                     FLAGS_max_vector_size)
+                     {false, static_cast<size_t>(FLAGS_max_vector_size)})
               << '\n';
   }
 }
diff --git a/aos/config_flattener.cc b/aos/config_flattener.cc
index 919028b..ee2f376 100644
--- a/aos/config_flattener.cc
+++ b/aos/config_flattener.cc
@@ -28,14 +28,16 @@
   }
 
   const std::string merged_config = FlatbufferToJson(
-      &configuration::MergeConfiguration(config, schemas).message(), true);
+      &configuration::MergeConfiguration(config, schemas).message(),
+      {.multi_line = true});
 
   // TODO(austin): Figure out how to squash the schemas onto 1 line so it is
   // easier to read?
   VLOG(1) << "Flattened config is " << merged_config;
   util::WriteStringToFileOrDie(full_output, merged_config);
-  util::WriteStringToFileOrDie(stripped_output,
-                               FlatbufferToJson(&config.message(), true));
+  util::WriteStringToFileOrDie(
+      stripped_output,
+      FlatbufferToJson(&config.message(), {.multi_line = true}));
   return 0;
 }
 
diff --git a/aos/configuration_test.cc b/aos/configuration_test.cc
index aa6de8e..c800017 100644
--- a/aos/configuration_test.cc
+++ b/aos/configuration_test.cc
@@ -27,18 +27,19 @@
     "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5 }";
 // And for multinode setups
 const char *kExpectedMultinodeLocation =
-    "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5, \"source_node\": \"pi1\" }";
+    "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5, "
+    "\"source_node\": \"pi1\" }";
 
 // Tests that we can read and merge a configuration.
 TEST_F(ConfigurationTest, ConfigMerge) {
   FlatbufferDetachedBuffer<Configuration> config =
       ReadConfig(kConfigPrefix + "config1.json");
-  LOG(INFO) << "Read: " << FlatbufferToJson(config, true);
+  LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
 
   EXPECT_EQ(
       absl::StripSuffix(
           util::ReadFileToStringOrDie(kConfigPrefix + "expected.json"), "\n"),
-      FlatbufferToJson(config, true));
+      FlatbufferToJson(config, {.multi_line = true}));
 }
 
 // Tests that we can get back a ChannelIndex.
@@ -55,13 +56,13 @@
 TEST_F(ConfigurationTest, ConfigMergeMultinode) {
   FlatbufferDetachedBuffer<Configuration> config =
       ReadConfig(kConfigPrefix + "config1_multinode.json");
-  LOG(INFO) << "Read: " << FlatbufferToJson(config, true);
+  LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
 
-  EXPECT_EQ(
-      std::string(absl::StripSuffix(
-          util::ReadFileToStringOrDie(kConfigPrefix + "expected_multinode.json"),
-          "\n")),
-      FlatbufferToJson(config, true));
+  EXPECT_EQ(std::string(absl::StripSuffix(
+                util::ReadFileToStringOrDie(kConfigPrefix +
+                                            "expected_multinode.json"),
+                "\n")),
+            FlatbufferToJson(config, {.multi_line = true}));
 }
 
 // Tests that we sort the entries in a config so we can look entries up.
@@ -69,7 +70,7 @@
   FlatbufferDetachedBuffer<Configuration> config =
       ReadConfig(kConfigPrefix + "backwards.json");
 
-  LOG(INFO) << "Read: " << FlatbufferToJson(config, true);
+  LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
 
   EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/aos/robot_state",
                                         "aos.RobotState", "app1", nullptr)),
@@ -115,7 +116,7 @@
 TEST_F(ConfigurationTest, MergeWithConfig) {
   FlatbufferDetachedBuffer<Configuration> config =
       ReadConfig(kConfigPrefix + "config1.json");
-  LOG(INFO) << "Read: " << FlatbufferToJson(config, true);
+  LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
 
   FlatbufferDetachedBuffer<Configuration> updated_config =
       MergeWithConfig(&config.message(),
@@ -129,11 +130,10 @@
   ]
 })channel");
 
-  EXPECT_EQ(
-      absl::StripSuffix(util::ReadFileToStringOrDie(
-                            kConfigPrefix + "expected_merge_with.json"),
-                        "\n"),
-      FlatbufferToJson(updated_config, true));
+  EXPECT_EQ(absl::StripSuffix(util::ReadFileToStringOrDie(
+                                  kConfigPrefix + "expected_merge_with.json"),
+                              "\n"),
+            FlatbufferToJson(updated_config, {.multi_line = true}));
 }
 
 // Tests that we can lookup a location, complete with maps, from a merged
@@ -187,9 +187,9 @@
       kExpectedMultinodeLocation);
 
   // Tests that a root map/rename works with a node specific map.
-  EXPECT_EQ(FlatbufferToJson(
-                GetChannel(config, "/batman", ".aos.bar", "app1", pi1)),
-            kExpectedMultinodeLocation);
+  EXPECT_EQ(
+      FlatbufferToJson(GetChannel(config, "/batman", ".aos.bar", "app1", pi1)),
+      kExpectedMultinodeLocation);
 
   // Tests that a root map/rename fails with a node specific map for the wrong
   // node.
@@ -427,9 +427,8 @@
 })channel",
           Channel::MiniReflectTypeTable()));
 
-  FlatbufferDetachedBuffer<Channel> logged_on_both_channel (
-      JsonToFlatbuffer(
-          R"channel({
+  FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
+      R"channel({
   "name": "/test",
   "type": "aos.examples.Ping",
   "source_node": "bar",
@@ -441,7 +440,7 @@
     }
   ]
 })channel",
-          Channel::MiniReflectTypeTable()));
+      Channel::MiniReflectTypeTable()));
 
   FlatbufferDetachedBuffer<Node> foo_node(JsonToFlatbuffer(
       R"node({
@@ -473,7 +472,7 @@
   EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
                                             &foo_node.message()));
   EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
-                                           &bar_node.message()));
+                                            &bar_node.message()));
   EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
                                             &baz_node.message()));
 
@@ -567,9 +566,8 @@
 })channel",
           Channel::MiniReflectTypeTable()));
 
-  FlatbufferDetachedBuffer<Channel> logged_on_both_channel (
-      JsonToFlatbuffer(
-          R"channel({
+  FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
+      R"channel({
   "name": "/test",
   "type": "aos.examples.Ping",
   "source_node": "bar",
@@ -581,7 +579,7 @@
     }
   ]
 })channel",
-          Channel::MiniReflectTypeTable()));
+      Channel::MiniReflectTypeTable()));
 
   FlatbufferDetachedBuffer<Node> foo_node(JsonToFlatbuffer(
       R"node({
@@ -783,10 +781,10 @@
                        GetNodeFromHostname(&config.message(), "raspberrypi3"))
                        ->name()
                        ->string_view());
-  EXPECT_EQ("pi2", CHECK_NOTNULL(
-                       GetNodeFromHostname(&config.message(), "other"))
-                       ->name()
-                       ->string_view());
+  EXPECT_EQ("pi2",
+            CHECK_NOTNULL(GetNodeFromHostname(&config.message(), "other"))
+                ->name()
+                ->string_view());
   EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "raspberrypi4"));
   EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "localhost"));
   EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "3"));
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index d0ca3ee..dc7c5d0 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -767,7 +767,7 @@
 
   // Confirm that we have the right number of reports, and the contents are
   // sane.
-  VLOG(1) << FlatbufferToJson(report, true);
+  VLOG(1) << FlatbufferToJson(report, {.multi_line = true});
 
   EXPECT_EQ(report.message().name()->string_view(), "primary");
 
@@ -1181,7 +1181,7 @@
     }
   }
 
-  VLOG(1) << FlatbufferToJson(report, true);
+  VLOG(1) << FlatbufferToJson(report, {.multi_line = true});
 
   EXPECT_EQ(report.message().name()->string_view(), "primary");
 
@@ -1245,7 +1245,7 @@
     }
   }
 
-  LOG(INFO) << FlatbufferToJson(primary_report, true);
+  LOG(INFO) << FlatbufferToJson(primary_report, {.multi_line = true});
 
   EXPECT_EQ(primary_report.message().name()->string_view(), "primary");
 
@@ -1327,7 +1327,7 @@
   }
 
   // Check the watcher report.
-  VLOG(1) << FlatbufferToJson(primary_report, true);
+  VLOG(1) << FlatbufferToJson(primary_report, {.multi_line = true});
 
   EXPECT_EQ(primary_report.message().name()->string_view(), "primary");
 
@@ -1397,7 +1397,7 @@
     }
   }
 
-  VLOG(1) << FlatbufferToJson(primary_report, true);
+  VLOG(1) << FlatbufferToJson(primary_report, {.multi_line = true});
 
   EXPECT_EQ(primary_report.message().name()->string_view(), "primary");
 
diff --git a/aos/events/logging/log_cat.cc b/aos/events/logging/log_cat.cc
index a1696eb..d3095cb 100644
--- a/aos/events/logging/log_cat.cc
+++ b/aos/events/logging/log_cat.cc
@@ -45,7 +45,7 @@
               << aos::FlatbufferToJson(
                      channel->schema(),
                      static_cast<const uint8_t *>(context.data),
-                     FLAGS_max_vector_size)
+                     {false, static_cast<size_t>(FLAGS_max_vector_size)})
               << std::endl;
   } else {
     std::cout << node_name << context.realtime_event_time << " ("
@@ -55,7 +55,7 @@
               << aos::FlatbufferToJson(
                      channel->schema(),
                      static_cast<const uint8_t *>(context.data),
-                     FLAGS_max_vector_size)
+                     {false, static_cast<size_t>(FLAGS_max_vector_size)})
               << std::endl;
   }
 }
@@ -93,16 +93,20 @@
 
       if (FLAGS_format_raw && message.value().message().data() != nullptr) {
         std::cout << aos::configuration::StrippedChannelToString(channel) << " "
-                  << aos::FlatbufferToJson(message.value(), false, 4) << ": "
+                  << aos::FlatbufferToJson(
+                         message.value(),
+                         {.multi_line = false, .max_vector_size = 4})
+                  << ": "
                   << aos::FlatbufferToJson(
                          channel->schema(),
                          message.value().message().data()->data(),
-                         FLAGS_max_vector_size)
+                         {false, static_cast<size_t>(FLAGS_max_vector_size)})
                   << std::endl;
       } else {
         std::cout << aos::configuration::StrippedChannelToString(channel) << " "
-                  << aos::FlatbufferToJson(message.value(), false,
-                                           FLAGS_max_vector_size)
+                  << aos::FlatbufferToJson(
+                         message.value(),
+                         {false, static_cast<size_t>(FLAGS_max_vector_size)})
                   << std::endl;
       }
     }
diff --git a/aos/events/logging/log_edit.cc b/aos/events/logging/log_edit.cc
index ff263f8..0dca904 100644
--- a/aos/events/logging/log_edit.cc
+++ b/aos/events/logging/log_edit.cc
@@ -18,8 +18,7 @@
     "If provided, this is the path to the JSON with the log file header.");
 
 int main(int argc, char **argv) {
-  gflags::SetUsageMessage(
-      R"(This tool lets us manipulate log files.)");
+  gflags::SetUsageMessage(R"(This tool lets us manipulate log files.)");
   aos::InitGoogle(&argc, &argv);
 
   if (!FLAGS_header.empty()) {
@@ -53,7 +52,8 @@
     } else {
       aos::logger::MessageReader reader(FLAGS_logfile);
       aos::util::WriteStringToFileOrDie(
-          FLAGS_header, aos::FlatbufferToJson(reader.log_file_header(), true));
+          FLAGS_header, aos::FlatbufferToJson(reader.log_file_header(),
+                                              {.multi_line = true}));
     }
   }
 
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index 0b5dbec..513dc1b 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -72,9 +72,8 @@
                      [&counter]() { counter += 1; });
   scheduler_scheduler.Run();
   EXPECT_EQ(counter, 1);
-  auto token =
-      scheduler.Schedule(monotonic_clock::epoch() + chrono::seconds(2),
-                         [&counter]() { counter += 1; });
+  auto token = scheduler.Schedule(monotonic_clock::epoch() + chrono::seconds(2),
+                                  [&counter]() { counter += 1; });
   scheduler.Deschedule(token);
   scheduler_scheduler.Run();
   EXPECT_EQ(counter, 1);
@@ -189,7 +188,7 @@
   }
 
   // Check the watcher report.
-  VLOG(1) << FlatbufferToJson(primary_report, true);
+  VLOG(1) << FlatbufferToJson(primary_report, {.multi_line = true});
 
   EXPECT_EQ(primary_report.message().name()->string_view(), "primary");
 
diff --git a/aos/flatbuffer_introspection.cc b/aos/flatbuffer_introspection.cc
index ae402fe..48200b7 100644
--- a/aos/flatbuffer_introspection.cc
+++ b/aos/flatbuffer_introspection.cc
@@ -70,8 +70,8 @@
     const reflection::Object *obj,
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
-    const ObjT *object, size_t max_vector_size, std::stringstream *out,
-    bool multi_line = false, int tree_depth = 0);
+    const ObjT *object, std::stringstream *out, JsonOptions json_options,
+    int tree_depth = 0);
 
 // Get enum value name
 const char *EnumToString(
@@ -141,8 +141,7 @@
     const ObjT *table, const reflection::Field *field,
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
-    size_t max_vector_size, std::stringstream *out, bool multi_line,
-    int tree_depth) {
+    std::stringstream *out, JsonOptions json_options, int tree_depth) {
   const reflection::Type *type = field->type();
 
   switch (type->base_type()) {
@@ -205,7 +204,7 @@
             flatbuffers::GetFieldAnyV(*table, *field);
         reflection::BaseType elem_type = type->element();
 
-        if (vector->size() > max_vector_size) {
+        if (vector->size() > json_options.max_vector_size) {
           *out << "[ ... " << vector->size() << " elements ... ]";
           break;
         }
@@ -213,7 +212,7 @@
         bool wrap = false;
         const int child_tree_depth = tree_depth + 1;
 
-        if (multi_line) {
+        if (json_options.multi_line) {
           wrap = ShouldCauseWrapping(elem_type);
         }
 
@@ -248,13 +247,12 @@
                     flatbuffers::GetAnyVectorElemAddressOf<
                         const flatbuffers::Struct>(
                         vector, i, objects->Get(type->index())->bytesize()),
-                    max_vector_size, out, multi_line, child_tree_depth);
+                    out, json_options, child_tree_depth);
               } else {
                 ObjectToString(objects->Get(type->index()), objects, enums,
                                flatbuffers::GetAnyVectorElemPointer<
                                    const flatbuffers::Table>(vector, i),
-                               max_vector_size, out, multi_line,
-                               child_tree_depth);
+                               out, json_options, child_tree_depth);
               }
             }
           }
@@ -271,12 +269,12 @@
       if (type->index() > -1 && type->index() < (int32_t)objects->size()) {
         if (objects->Get(type->index())->is_struct()) {
           ObjectToString(objects->Get(type->index()), objects, enums,
-                         flatbuffers::GetFieldStruct(*table, *field),
-                         max_vector_size, out, multi_line, tree_depth);
+                         flatbuffers::GetFieldStruct(*table, *field), out,
+                         json_options, tree_depth);
         } else if constexpr (std::is_same<flatbuffers::Table, ObjT>()) {
           ObjectToString(objects->Get(type->index()), objects, enums,
-                         flatbuffers::GetFieldT(*table, *field),
-                         max_vector_size, out, multi_line, tree_depth);
+                         flatbuffers::GetFieldT(*table, *field), out,
+                         json_options, tree_depth);
         }
       } else {
         *out << "null";
@@ -294,8 +292,8 @@
     const reflection::Object *obj,
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> *objects,
     const flatbuffers::Vector<flatbuffers::Offset<reflection::Enum>> *enums,
-    const ObjT *object, size_t max_vector_size, std::stringstream *out,
-    bool multi_line, int tree_depth) {
+    const ObjT *object, std::stringstream *out, JsonOptions json_options,
+    int tree_depth) {
   static_assert(std::is_same<flatbuffers::Table, ObjT>() ||
                     std::is_same<flatbuffers::Struct, ObjT>(),
                 "Type must be either flatbuffer table or struct");
@@ -304,7 +302,7 @@
   const int child_tree_depth = tree_depth + 1;
 
   bool wrap = false;
-  if (multi_line) {
+  if (json_options.multi_line) {
     // Check whether this object has objects, vectors, or floats inside of it
     for (const reflection::Field *field : *obj->fields()) {
       if (ShouldCauseWrapping(field->type()->base_type())) {
@@ -334,8 +332,8 @@
       }
 
       *out << '"' << field->name()->c_str() << "\": ";
-      FieldToString(object, field, objects, enums, max_vector_size, out,
-                    multi_line, child_tree_depth);
+      FieldToString(object, field, objects, enums, out, json_options,
+                    child_tree_depth);
     }
   }
 
@@ -349,8 +347,7 @@
 }  // namespace
 
 std::string FlatbufferToJson(const reflection::Schema *schema,
-                             const uint8_t *data, bool multi_line,
-                             size_t max_vector_size) {
+                             const uint8_t *data, JsonOptions json_options) {
   CHECK(schema != nullptr) << ": Need to provide a schema";
 
   // It is pretty common to get passed in a nullptr when a test fails.  Rather
@@ -365,8 +362,8 @@
 
   std::stringstream out;
 
-  ObjectToString(obj, schema->objects(), schema->enums(), table,
-                 max_vector_size, &out, multi_line);
+  ObjectToString(obj, schema->objects(), schema->enums(), table, &out,
+                 json_options);
 
   return out.str();
 }
diff --git a/aos/flatbuffer_introspection_test.cc b/aos/flatbuffer_introspection_test.cc
index 9dacd41..d771b55 100644
--- a/aos/flatbuffer_introspection_test.cc
+++ b/aos/flatbuffer_introspection_test.cc
@@ -358,7 +358,8 @@
   builder.Finish(config_builder.Finish());
 
   std::string out =
-      FlatbufferToJson(schema_, builder.GetBufferPointer(), false, 100);
+      FlatbufferToJson(schema_, builder.GetBufferPointer(),
+                       {.multi_line = false, .max_vector_size = 100});
   EXPECT_EQ(out, "{\"vector_foo_int\": [ ... 101 elements ... ]}");
 }
 
@@ -371,7 +372,8 @@
 
   builder.Finish(config_builder.Finish());
 
-  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(), true);
+  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(),
+                                     {.multi_line = true});
 
   EXPECT_EQ(out,
             "{\n"
@@ -391,7 +393,8 @@
 
   builder.Finish(config_builder.Finish());
 
-  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(), true);
+  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(),
+                                     {.multi_line = true});
 
   EXPECT_EQ(out,
             "{\n"
@@ -415,7 +418,8 @@
 
   builder.Finish(config_builder.Finish());
 
-  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(), true);
+  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(),
+                                     {.multi_line = true});
 
   EXPECT_EQ(out,
             "{\n"
@@ -453,7 +457,8 @@
 
   builder.Finish(config_builder.Finish());
 
-  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(), true);
+  std::string out = FlatbufferToJson(schema_, builder.GetBufferPointer(),
+                                     {.multi_line = true});
 
   EXPECT_EQ(out,
             "{\n"
diff --git a/aos/json_to_flatbuffer.cc b/aos/json_to_flatbuffer.cc
index fdab330..9980328 100644
--- a/aos/json_to_flatbuffer.cc
+++ b/aos/json_to_flatbuffer.cc
@@ -790,7 +790,7 @@
 
 ::std::string BufferFlatbufferToJson(const uint8_t *buffer,
                                      const ::flatbuffers::TypeTable *typetable,
-                                     bool multi_line, size_t max_vector_size) {
+                                     JsonOptions json_options) {
   // It is pretty common to get passed in a nullptr when a test fails.  Rather
   // than CHECK, return a more user friendly result.
   if (buffer == nullptr) {
@@ -798,7 +798,7 @@
   }
   return TableFlatbufferToJson(reinterpret_cast<const flatbuffers::Table *>(
                                    flatbuffers::GetRoot<uint8_t>(buffer)),
-                               typetable, multi_line, max_vector_size);
+                               typetable, json_options);
 }
 
 namespace {
@@ -925,15 +925,15 @@
 
 ::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
                                     const ::flatbuffers::TypeTable *typetable,
-                                    bool multi_line, size_t max_vector_size) {
+                                    JsonOptions json_options) {
   // It is pretty common to get passed in a nullptr when a test fails.  Rather
   // than CHECK, return a more user friendly result.
   if (t == nullptr) {
     return "null";
   }
-  TruncatingStringVisitor tostring_visitor(max_vector_size,
-                                           multi_line ? "\n" : " ", true,
-                                           multi_line ? " " : "", multi_line);
+  TruncatingStringVisitor tostring_visitor(
+      json_options.max_vector_size, json_options.multi_line ? "\n" : " ", true,
+      json_options.multi_line ? " " : "", json_options.multi_line);
   flatbuffers::IterateObject(reinterpret_cast<const uint8_t *>(t), typetable,
                              &tostring_visitor);
   return tostring_visitor.string();
diff --git a/aos/json_to_flatbuffer.h b/aos/json_to_flatbuffer.h
index b765db3..aebd069 100644
--- a/aos/json_to_flatbuffer.h
+++ b/aos/json_to_flatbuffer.h
@@ -34,53 +34,53 @@
       JsonToFlatbuffer(data, T::MiniReflectTypeTable(), fbb).o);
 }
 
+struct JsonOptions {
+  // controls if the Json is written ouut on multiple lines or one.
+  bool multi_line = false;
+  // the contents of vectors longer than max_vector_size will be skipped.
+  size_t max_vector_size = SIZE_MAX;
+};
+
 // Converts a flatbuffer into a Json string.
-// multi_line controls if the Json is written out on multiple lines or one.
 // The methods below are generally more useful than BufferFlatbufferToJson and
 // TableFlatbufferToJson.
 ::std::string BufferFlatbufferToJson(const uint8_t *buffer,
                                      const flatbuffers::TypeTable *typetable,
-                                     bool multi_line = false,
-                                     size_t max_vector_size = SIZE_MAX);
+                                     JsonOptions json_options = {});
 
 ::std::string TableFlatbufferToJson(const flatbuffers::Table *t,
                                     const ::flatbuffers::TypeTable *typetable,
-                                    bool multi_line,
-                                    size_t max_vector_size = SIZE_MAX);
+                                    JsonOptions json_options = {});
 
 // Converts a DetachedBuffer holding a flatbuffer to JSON.
 inline ::std::string FlatbufferToJson(const flatbuffers::DetachedBuffer &buffer,
                                       const flatbuffers::TypeTable *typetable,
-                                      bool multi_line = false,
-                                      size_t max_vector_size = SIZE_MAX) {
-  return BufferFlatbufferToJson(buffer.data(), typetable, multi_line,
-                                max_vector_size);
+                                      JsonOptions json_options = {}) {
+  return BufferFlatbufferToJson(buffer.data(), typetable, json_options);
 }
 
 // Converts a Flatbuffer<T> holding a flatbuffer to JSON.
 template <typename T>
 inline ::std::string FlatbufferToJson(const Flatbuffer<T> &flatbuffer,
-                                      bool multi_line = false,
-                                      size_t max_vector_size = SIZE_MAX) {
-  return BufferFlatbufferToJson(flatbuffer.data(),
-                                Flatbuffer<T>::MiniReflectTypeTable(),
-                                multi_line, max_vector_size);
+                                      JsonOptions json_options = {}) {
+  return BufferFlatbufferToJson(
+      flatbuffer.data(), Flatbuffer<T>::MiniReflectTypeTable(), json_options);
 }
 
 // Converts a flatbuffer::Table to JSON.
 template <typename T>
-typename std::enable_if<std::is_base_of<flatbuffers::Table, T>::value,
-                        std::string>::
-    type inline FlatbufferToJson(const T *flatbuffer, bool multi_line = false,
-                                 size_t max_vector_size = SIZE_MAX) {
+typename std::enable_if<
+    std::is_base_of<flatbuffers::Table, T>::value,
+    std::string>::type inline FlatbufferToJson(const T *flatbuffer,
+                                               JsonOptions json_options = {}) {
   return TableFlatbufferToJson(
       reinterpret_cast<const flatbuffers::Table *>(flatbuffer),
-      Flatbuffer<T>::MiniReflectTypeTable(), multi_line, max_vector_size);
+      Flatbuffer<T>::MiniReflectTypeTable(), json_options);
 }
 
 std::string FlatbufferToJson(const reflection::Schema *const schema,
-                             const uint8_t *const data, bool multi_line = false,
-                             size_t max_vector_size = SIZE_MAX);
+                             const uint8_t *const data,
+                             JsonOptions json_options = {});
 
 // Writes a Flatbuffer to a file, or dies.
 template <typename T>
diff --git a/aos/json_to_flatbuffer_test.cc b/aos/json_to_flatbuffer_test.cc
index 6dbb7ea..80dec7c 100644
--- a/aos/json_to_flatbuffer_test.cc
+++ b/aos/json_to_flatbuffer_test.cc
@@ -78,7 +78,6 @@
   EXPECT_TRUE(JsonAndBack("{ \"foo_double\": 5.1 }"));
 }
 
-
 // Test what happens if you pass a field name that we don't know.
 TEST_F(JsonToFlatbufferTest, InvalidFieldName) {
   EXPECT_FALSE(JsonAndBack("{ \"foo\": 5 }"));
@@ -156,7 +155,8 @@
 
   EXPECT_TRUE(JsonAndBack("{ \"vector_foo_string\": [ \"bar\", \"baz\" ] }"));
   EXPECT_TRUE(JsonAndBack("{ \"vector_foo_string\": [  ] }"));
-  EXPECT_TRUE(JsonAndBack("{ \"vector_foo_enum\": [ \"None\", \"UType\", \"Bool\" ] }"));
+  EXPECT_TRUE(JsonAndBack(
+      "{ \"vector_foo_enum\": [ \"None\", \"UType\", \"Bool\" ] }"));
   EXPECT_TRUE(JsonAndBack("{ \"vector_foo_enum\": [  ] }"));
 }
 
@@ -170,8 +170,7 @@
   EXPECT_TRUE(JsonAndBack(
       "{ \"apps\": [ { \"name\": \"woot\" }, { \"name\": \"wow\" } ] }"));
 
-  EXPECT_TRUE(JsonAndBack(
-      "{ \"apps\": [ {  }, {  } ] }"));
+  EXPECT_TRUE(JsonAndBack("{ \"apps\": [ {  }, {  } ] }"));
 }
 
 // Test that we can parse an empty message.
@@ -198,7 +197,8 @@
 // Tests that multiple arrays get properly handled.
 TEST_F(JsonToFlatbufferTest, NestedArrays) {
   EXPECT_TRUE(
-      JsonAndBack("{ \"vov\": { \"v\": [ { \"str\": [ \"a\", \"b\" ] }, { \"str\": [ \"c\", \"d\" ] } ] } }"));
+      JsonAndBack("{ \"vov\": { \"v\": [ { \"str\": [ \"a\", \"b\" ] }, { "
+                  "\"str\": [ \"c\", \"d\" ] } ] } }"));
 }
 
 // TODO(austin): Missmatched values.
@@ -222,10 +222,10 @@
       JsonToFlatbuffer(json_long.data(), ConfigurationTypeTable());
   ASSERT_GT(fb_long.size(), 0);
 
-  const std::string back_json_short =
-      FlatbufferToJson(fb_short, ConfigurationTypeTable(), false, 100);
-  const std::string back_json_long =
-      FlatbufferToJson(fb_long, ConfigurationTypeTable(), false, 100);
+  const std::string back_json_short = FlatbufferToJson(
+      fb_short, ConfigurationTypeTable(), {.multi_line = false, .max_vector_size = 100});
+  const std::string back_json_long = FlatbufferToJson(
+      fb_long, ConfigurationTypeTable(), {.multi_line = false, .max_vector_size = 100});
 
   EXPECT_EQ(json_short, back_json_short);
   EXPECT_EQ("{ \"vector_foo_int\": [ ... 101 elements ... ] }", back_json_long);
@@ -234,7 +234,7 @@
 // Tests that a nullptr buffer prints nullptr.
 TEST_F(JsonToFlatbufferTest, NullptrData) {
   EXPECT_EQ("null", TableFlatbufferToJson((const flatbuffers::Table *)(nullptr),
-                                          ConfigurationTypeTable(), false));
+                                          ConfigurationTypeTable()));
 }
 
 }  // namespace testing
diff --git a/y2020/vision/calibration.cc b/y2020/vision/calibration.cc
index 15f1f4c..6cf9e8e 100644
--- a/y2020/vision/calibration.cc
+++ b/y2020/vision/calibration.cc
@@ -96,7 +96,6 @@
 
 namespace {
 
-
 void ViewerMain() {
   aos::FlatbufferDetachedBuffer<aos::Configuration> config =
       aos::configuration::ReadConfig(FLAGS_config);
@@ -139,107 +138,104 @@
       absl::StrCat("/pi", std::to_string(pi_number.value()), "/camera");
   LOG(INFO) << "Connecting to channel " << channel;
 
-  event_loop.MakeWatcher(
-      channel, [&event_loop, &board, &all_charuco_ids, &all_charuco_corners,
-                camera_matrix, dist_coeffs,
-                eigen_camera_matrix](const CameraImage &image) {
-        const aos::monotonic_clock::duration age =
-            event_loop.monotonic_now() -
-            event_loop.context().monotonic_event_time;
-        const double age_double =
-            std::chrono::duration_cast<std::chrono::duration<double>>(age)
-                .count();
-        if (age > std::chrono::milliseconds(100)) {
-          LOG(INFO) << "Age: " << age_double << ", getting behind, skipping";
-          return;
-        }
-        // Create color image:
-        cv::Mat image_color_mat(cv::Size(image.cols(), image.rows()), CV_8UC2,
-                                (void *)image.data()->data());
-        const cv::Size image_size(image.cols(), image.rows());
-        cv::Mat rgb_image(image_size, CV_8UC3);
-        cv::cvtColor(image_color_mat, rgb_image, CV_YUV2BGR_YUYV);
+  event_loop.MakeWatcher(channel, [&event_loop, &board, &all_charuco_ids,
+                                   &all_charuco_corners, camera_matrix,
+                                   dist_coeffs, eigen_camera_matrix](
+                                      const CameraImage &image) {
+    const aos::monotonic_clock::duration age =
+        event_loop.monotonic_now() - event_loop.context().monotonic_event_time;
+    const double age_double =
+        std::chrono::duration_cast<std::chrono::duration<double>>(age).count();
+    if (age > std::chrono::milliseconds(100)) {
+      LOG(INFO) << "Age: " << age_double << ", getting behind, skipping";
+      return;
+    }
+    // Create color image:
+    cv::Mat image_color_mat(cv::Size(image.cols(), image.rows()), CV_8UC2,
+                            (void *)image.data()->data());
+    const cv::Size image_size(image.cols(), image.rows());
+    cv::Mat rgb_image(image_size, CV_8UC3);
+    cv::cvtColor(image_color_mat, rgb_image, CV_YUV2BGR_YUYV);
 
-        std::vector<int> marker_ids;
-        std::vector<std::vector<cv::Point2f>> marker_corners;
+    std::vector<int> marker_ids;
+    std::vector<std::vector<cv::Point2f>> marker_corners;
 
-        cv::aruco::detectMarkers(rgb_image, board->dictionary, marker_corners,
-                                 marker_ids);
+    cv::aruco::detectMarkers(rgb_image, board->dictionary, marker_corners,
+                             marker_ids);
 
-        std::vector<cv::Point2f> charuco_corners;
-        std::vector<int> charuco_ids;
-        // If at least one marker detected
-        if (marker_ids.size() >= FLAGS_min_targets) {
-          // Run everything twice, once with the calibration, and once without.
-          // This lets us both calibrate, and also print out the pose real time
-          // with the previous calibration.
-          cv::aruco::interpolateCornersCharuco(marker_corners, marker_ids,
-                                               rgb_image, board,
-                                               charuco_corners, charuco_ids);
+    std::vector<cv::Point2f> charuco_corners;
+    std::vector<int> charuco_ids;
+    // If at least one marker detected
+    if (marker_ids.size() >= FLAGS_min_targets) {
+      // Run everything twice, once with the calibration, and once without.
+      // This lets us both calibrate, and also print out the pose real time
+      // with the previous calibration.
+      cv::aruco::interpolateCornersCharuco(marker_corners, marker_ids,
+                                           rgb_image, board, charuco_corners,
+                                           charuco_ids);
 
-          std::vector<cv::Point2f> charuco_corners_with_calibration;
-          std::vector<int> charuco_ids_with_calibration;
+      std::vector<cv::Point2f> charuco_corners_with_calibration;
+      std::vector<int> charuco_ids_with_calibration;
 
-          cv::aruco::interpolateCornersCharuco(
-              marker_corners, marker_ids, rgb_image, board,
-              charuco_corners_with_calibration, charuco_ids_with_calibration,
-              camera_matrix, dist_coeffs);
+      cv::aruco::interpolateCornersCharuco(
+          marker_corners, marker_ids, rgb_image, board,
+          charuco_corners_with_calibration, charuco_ids_with_calibration,
+          camera_matrix, dist_coeffs);
 
-          cv::aruco::drawDetectedMarkers(rgb_image, marker_corners, marker_ids);
+      cv::aruco::drawDetectedMarkers(rgb_image, marker_corners, marker_ids);
 
-          if (charuco_ids.size() >= FLAGS_min_targets) {
-            cv::aruco::drawDetectedCornersCharuco(
-                rgb_image, charuco_corners, charuco_ids, cv::Scalar(255, 0, 0));
+      if (charuco_ids.size() >= FLAGS_min_targets) {
+        cv::aruco::drawDetectedCornersCharuco(
+            rgb_image, charuco_corners, charuco_ids, cv::Scalar(255, 0, 0));
 
-            cv::Vec3d rvec, tvec;
-            bool valid = cv::aruco::estimatePoseCharucoBoard(
-                charuco_corners_with_calibration, charuco_ids_with_calibration,
-                board, camera_matrix, dist_coeffs, rvec, tvec);
+        cv::Vec3d rvec, tvec;
+        bool valid = cv::aruco::estimatePoseCharucoBoard(
+            charuco_corners_with_calibration, charuco_ids_with_calibration,
+            board, camera_matrix, dist_coeffs, rvec, tvec);
 
-
-            // if charuco pose is valid
-            if (valid) {
-              LOG(INFO) << std::fixed << std::setprecision(6)
-                        << "Age: " << age_double << ", Pose is R:" << rvec
-                        << " T:" << tvec;
-              cv::aruco::drawAxis(rgb_image, camera_matrix,
-                                  dist_coeffs, rvec, tvec, 0.1);
-            } else {
-              LOG(INFO) << "Age: " << age_double << ", invalid pose";
-            }
-          } else {
-            LOG(INFO) << "Age: " << age_double << ", no charuco IDs.";
-          }
+        // if charuco pose is valid
+        if (valid) {
+          LOG(INFO) << std::fixed << std::setprecision(6)
+                    << "Age: " << age_double << ", Pose is R:" << rvec
+                    << " T:" << tvec;
+          cv::aruco::drawAxis(rgb_image, camera_matrix, dist_coeffs, rvec, tvec,
+                              0.1);
         } else {
-          LOG(INFO) << "Age: " << age_double << ", no marker IDs";
+          LOG(INFO) << "Age: " << age_double << ", invalid pose";
         }
+      } else {
+        LOG(INFO) << "Age: " << age_double << ", no charuco IDs.";
+      }
+    } else {
+      LOG(INFO) << "Age: " << age_double << ", no marker IDs";
+    }
 
-        cv::imshow("Display", rgb_image);
+    cv::imshow("Display", rgb_image);
 
-        if (FLAGS_display_undistorted) {
-          cv::Mat undistorted_rgb_image(image_size, CV_8UC3);
-          cv::undistort(rgb_image, undistorted_rgb_image, camera_matrix,
-                        dist_coeffs);
+    if (FLAGS_display_undistorted) {
+      cv::Mat undistorted_rgb_image(image_size, CV_8UC3);
+      cv::undistort(rgb_image, undistorted_rgb_image, camera_matrix,
+                    dist_coeffs);
 
-          cv::imshow("Display undist", undistorted_rgb_image);
-        }
+      cv::imshow("Display undist", undistorted_rgb_image);
+    }
 
-        int keystroke = cv::waitKey(1);
-        if ((keystroke & 0xFF) == static_cast<int>('c')) {
-          if (charuco_ids.size() >= FLAGS_min_targets) {
-            all_charuco_ids.emplace_back(std::move(charuco_ids));
-            all_charuco_corners.emplace_back(std::move(charuco_corners));
-            LOG(INFO) << "Image " << all_charuco_corners.size();
-          }
+    int keystroke = cv::waitKey(1);
+    if ((keystroke & 0xFF) == static_cast<int>('c')) {
+      if (charuco_ids.size() >= FLAGS_min_targets) {
+        all_charuco_ids.emplace_back(std::move(charuco_ids));
+        all_charuco_corners.emplace_back(std::move(charuco_corners));
+        LOG(INFO) << "Image " << all_charuco_corners.size();
+      }
 
-          if (all_charuco_ids.size() >= 50) {
-            LOG(INFO) << "Got enough images to calibrate";
-            event_loop.Exit();
-          }
-        } else if ((keystroke & 0xFF) == static_cast<int>('q')) {
-          event_loop.Exit();
-        }
-      });
+      if (all_charuco_ids.size() >= 50) {
+        LOG(INFO) << "Got enough images to calibrate";
+        event_loop.Exit();
+      }
+    } else if ((keystroke & 0xFF) == static_cast<int>('q')) {
+      event_loop.Exit();
+    }
+  });
 
   event_loop.Run();
 
@@ -302,10 +298,12 @@
                         pi_number.value(), time_ss.str());
 
     LOG(INFO) << calibration_filename << " -> "
-              << aos::FlatbufferToJson(camera_calibration, true);
+              << aos::FlatbufferToJson(camera_calibration,
+                                       {.multi_line = true});
 
     aos::util::WriteStringToFileOrDie(
-        calibration_filename, aos::FlatbufferToJson(camera_calibration, true));
+        calibration_filename,
+        aos::FlatbufferToJson(camera_calibration, {.multi_line = true}));
   } else {
     LOG(INFO) << "Skipping calibration due to not enough images.";
   }