Refactor channel remapping out of LogReader

This change takes all the logic to rename and remap channels in
LogReader and places it in a new class: ConfigRemapper. LogReader still
retains the same remapping/renaming API for backwards compatibility, but
now the args are basically just passed along to ConfigRemapper. This was
done to allow configs to be remapped without a corresponding log file.

Change-Id: Ia32d8a3e640e94af0b52397ccd322149588d6da4
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/events/logging/config_remapper.h b/aos/events/logging/config_remapper.h
new file mode 100644
index 0000000..d1eb0c6
--- /dev/null
+++ b/aos/events/logging/config_remapper.h
@@ -0,0 +1,196 @@
+#ifndef AOS_EVENTS_LOGGING_CONFIG_REMAPPER_H_
+#define AOS_EVENTS_LOGGING_CONFIG_REMAPPER_H_
+
+#include <map>
+#include <string_view>
+#include <tuple>
+#include <vector>
+
+#include "flatbuffers/flatbuffers.h"
+
+#include "aos/events/event_loop.h"
+#include "aos/events/logging/logger_generated.h"
+#include "aos/events/logging/replay_channels.h"
+
+namespace aos {
+
+// This class is used for remapping and renaming channels with the passed in
+// configuration to the constructor. Typically, the templated versions of
+// RemapOriginalChannel and RenameOriginalChannel are the main functions to use
+// for type safety. After remapping and renaming, the remapped configuration can
+// be accessed through remapped_configuration(), and the original configuration
+// that is not mutated can be accessed through original_configuration.
+//
+// This class assumes no ownership over any pointers provided to it.
+//
+// Timestamp channels are automatically remapped on construction
+//
+// Note: This class does not need logfiles to function unlike LogReader. This
+// logic originally lived in LogReader and was refactored out into this class.
+// The same API for remapping and renaming still exists in LogReader which now
+// just passes along the args to this class.
+class ConfigRemapper {
+ public:
+  ConfigRemapper(const Configuration *config,
+                 const Configuration *replay_config = nullptr,
+                 const logger::ReplayChannels *replay_channels = nullptr);
+  ~ConfigRemapper();
+
+  // Map of channel indices to new name. The channel index will be an index into
+  // original_configuration(), and the string key will be the name of the
+  // channel to send on instead of the orignal channel name.
+  struct RemappedChannel {
+    std::string remapped_name;
+    std::string new_type;
+  };
+
+  // Enum to use for indicating how RemapOriginalChannel behaves when there is
+  // already a channel with the remapped name (e.g., as may happen when
+  // replaying a logfile that was itself generated from replay).
+  enum class RemapConflict {
+    // LOG(FATAL) on conflicts in remappings.
+    kDisallow,
+    // If we run into a conflict, attempt to remap the channel we would be
+    // overriding (and continue to do so if remapping *that* channel also
+    // generates a conflict).
+    // This will mean that if we repeatedly replay a log, we will end up
+    // stacking more and more /original's on the start of the oldest version
+    // of the channels.
+    kCascade
+  };
+
+  // Remaps a channel from the original configuration passed to the constructor
+  // to the given one. This operates on raw channel names, without any node or
+  // application specific mappings.
+  void RemapOriginalChannel(
+      std::string_view name, std::string_view type,
+      std::string_view add_prefix = "/original", std::string_view new_type = "",
+      RemapConflict conflict_handling = RemapConflict::kCascade);
+  template <typename T>
+  void RemapOriginalChannel(
+      std::string_view name, std::string_view add_prefix = "/original",
+      std::string_view new_type = "",
+      RemapConflict conflict_handling = RemapConflict::kCascade) {
+    RemapOriginalChannel(name, T::GetFullyQualifiedName(), add_prefix, new_type,
+                         conflict_handling);
+  }
+
+  // Remaps the provided channel, though this respects node mappings, and
+  // preserves them too.  This makes it so if /aos -> /pi1/aos on one node,
+  // /original/aos -> /original/pi1/aos on the same node after renaming, just
+  // like you would hope.  If new_type is not empty, the new channel will use
+  // the provided type instead.  This allows for renaming messages.
+  //
+  // TODO(austin): If you have 2 nodes remapping something to the same channel,
+  // this doesn't handle that.  No use cases exist yet for that, so it isn't
+  // being done yet.
+  void RemapOriginalChannel(
+      std::string_view name, std::string_view type, const Node *node,
+      std::string_view add_prefix = "/original", std::string_view new_type = "",
+      RemapConflict conflict_handling = RemapConflict::kCascade);
+
+  template <typename T>
+  void RemapOriginalChannel(
+      std::string_view name, const Node *node,
+      std::string_view add_prefix = "/original", std::string_view new_type = "",
+      RemapConflict conflict_handling = RemapConflict::kCascade) {
+    RemapOriginalChannel(name, T::GetFullyQualifiedName(), node, add_prefix,
+                         new_type, conflict_handling);
+  }
+
+  // Similar to RemapOriginalChannel(), but lets you specify a name for the new
+  // channel without constraints. By default, this will not add any maps for the
+  // new channel. Use add_maps to specify any maps you'd like added.
+  void RenameOriginalChannel(std::string_view name, std::string_view type,
+                             std::string_view new_name,
+                             const std::vector<MapT> &add_maps = {});
+  template <typename T>
+  void RenameOriginalChannel(std::string_view name, std::string_view new_name,
+                             const std::vector<MapT> &add_maps = {}) {
+    RenameOriginalChannel(name, T::GetFullyQualifiedName(), new_name, add_maps);
+  }
+  // The following overloads are more suitable for multi-node configurations,
+  // and let you rename a channel on a specific node.
+  void RenameOriginalChannel(std::string_view name, std::string_view type,
+                             const Node *node, std::string_view new_name,
+                             const std::vector<MapT> &add_maps = {});
+  template <typename T>
+  void RenameOriginalChannel(std::string_view name, const Node *node,
+                             std::string_view new_name,
+                             const std::vector<MapT> &add_maps = {}) {
+    RenameOriginalChannel(name, T::GetFullyQualifiedName(), node, new_name,
+                          add_maps);
+  }
+
+  template <typename T>
+  bool HasChannel(std::string_view name, const Node *node = nullptr) {
+    return HasChannel(name, T::GetFullyQualifiedName(), node);
+  }
+  bool HasChannel(std::string_view name, std::string_view type,
+                  const Node *node) {
+    return configuration::GetChannel(original_configuration(), name, type, "",
+                                     node, true) != nullptr;
+  }
+
+  // Returns true if the channel exists on the node and was in the original
+  // config
+  template <typename T>
+  bool HasOriginalChannel(std::string_view name, const Node *node = nullptr) {
+    const Channel *channel =
+        configuration::GetChannel(original_configuration(), name,
+                                  T::GetFullyQualifiedName(), "", node, true);
+    if (channel == nullptr) return false;
+    return channel->logger() != LoggerConfig::NOT_LOGGED;
+  }
+
+  template <typename T>
+  void MaybeRemapOriginalChannel(std::string_view name,
+                                 const Node *node = nullptr) {
+    if (HasChannel<T>(name, node)) {
+      RemapOriginalChannel<T>(name, node);
+    }
+  }
+  template <typename T>
+  void MaybeRenameOriginalChannel(std::string_view name, const Node *node,
+                                  std::string_view new_name,
+                                  const std::vector<MapT> &add_maps = {}) {
+    if (HasChannel<T>(name, node)) {
+      RenameOriginalChannel<T>(name, node, new_name, add_maps);
+    }
+  }
+
+  const Channel *RemapChannel(const EventLoop *event_loop, const Node *node,
+                              const Channel *channel);
+  // Returns a list of all the original channels from remapping.
+  std::vector<const Channel *> RemappedChannels() const;
+
+  void set_configuration(const Configuration *configuration);
+
+  // Returns the configuration that was originally passed to the constructor.
+  // This class does not own this pointer.
+  const Configuration *original_configuration() const;
+
+  // Returns the configuration that contains the remapping and renamings done on
+  // the original configuration. The pointer is invalidated whenever
+  // RemapOriginalChannel is called.
+  const Configuration *remapped_configuration() const;
+
+ private:
+  // Handle constructing a configuration with all the additional remapped
+  // channels from calls to RemapOriginalChannel.
+  void MakeRemappedConfig();
+
+  std::map<size_t, RemappedChannel> remapped_channels_;
+  std::vector<MapT> maps_;
+  std::unique_ptr<FlatbufferDetachedBuffer<Configuration>>
+      remapped_configuration_buffer_;
+
+  const Configuration *remapped_configuration_ = nullptr;
+  const Configuration *original_configuration_ = nullptr;
+  const Configuration *replay_configuration_ = nullptr;
+
+  const logger::ReplayChannels *replay_channels_ = nullptr;
+};  // class ConfigRemapper
+
+}  // namespace aos
+#endif  // AOS_EVENTS_LOGGING_CONFIG_REMAPPER_H_