#s4v2 - Add functions to parse text proto directly from a file

PiperOrigin-RevId: 788085466
Change-Id: I831cb2790c37f00b3b6f03605bc933e14a530c39
diff --git a/bmc/machine_configs/setup_configs.cc b/bmc/machine_configs/setup_configs.cc
index 93514df..9f8854a 100644
--- a/bmc/machine_configs/setup_configs.cc
+++ b/bmc/machine_configs/setup_configs.cc
@@ -3,7 +3,6 @@
 
 #include "action_context.h"
 #include "bmc/machine_configs/embedded_data.h"
-#include "bmc/proto_reader.h"
 #include "bmc/register_actions_bmc.h"
 #include "bmc/state_monitor_bmc.h"
 #include "daemon_context.h"
@@ -12,7 +11,6 @@
 #include "absl/flags/flag.h"
 #include "absl/log/log.h"
 #include "absl/status/status.h"
-#include "absl/status/statusor.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "bmc/status_macros.h"
@@ -43,15 +41,6 @@
   return bakedin_config;
 }
 
-static absl::StatusOr<SafePowerAgentConfig> ReadLocalConfig(
-    absl::string_view config_file_path) {
-  ASSIGN_OR_RETURN(std::string config_proto_str,
-                   proto_reader::ReadFileToString(config_file_path));
-  LOG(INFO) << "Config proto " << config_proto_str;
-  return safepower_agent::ParseTextProto<SafePowerAgentConfig>(
-      config_proto_str);
-}
-
 // Load the configs provided
 static absl::Status Load(StateMonitorBMC& state_monitor,
                          ActionContextManager& action_context_manager) {
@@ -71,8 +60,9 @@
 absl::Status SetupConfigs(StateMonitorBMC& state_monitor,
                           ActionContextManager& action_context_manager,
                           absl::string_view local_config_path) {
-  absl::StatusOr<SafePowerAgentConfig> safepower_config =
-      ReadLocalConfig(local_config_path);
+  auto safepower_config =
+      safepower_agent::ParseTextProtoFromFile<SafePowerAgentConfig>(
+          std::string(local_config_path));
 
   absl::Status load_status;
   if (safepower_config.ok()) {
diff --git a/parse_text_proto.cc b/parse_text_proto.cc
index 34a97e6..f1a426e 100644
--- a/parse_text_proto.cc
+++ b/parse_text_proto.cc
@@ -1,6 +1,9 @@
 #include "parse_text_proto.h"
 
 #include <cstddef>
+#include <cstdio>
+#include <fstream>
+#include <string>
 #include <vector>
 
 #include "absl/log/log.h"
@@ -12,22 +15,37 @@
 namespace safepower_agent {
 namespace internal {
 
-static absl::string_view ExtractLine(absl::string_view text, int line) {
-  std::vector<absl::string_view> lines = absl::StrSplit(text, '\n');
-  return (static_cast<std::size_t>(line) < lines.size()) ? lines[line] : "";
+std::string ErrorCollectorString::ExtractLine(int line) const {
+  std::vector<absl::string_view> lines = absl::StrSplit(text_proto_, '\n');
+  return (static_cast<std::size_t>(line) < lines.size())
+             ? std::string(lines[line])
+             : "";
+}
+
+std::string ErrorCollectorFile::ExtractLine(int line) const {
+  std::ifstream file(path_);
+  std::string line_string;
+  for (int i = 0; i <= line; ++i) {
+    if (!file.good()) {
+      return "";
+    }
+    std::getline(file, line_string);
+    LOG(INFO) << "line: " << line_string;
+  }
+  return line_string;
 }
 
 void ErrorCollector::RecordError(int line, google::protobuf::io::ColumnNumber column,
                                  absl::string_view message) {
   absl::StrAppendFormat(&message_, "%s [line %d, column %d]\n%s", message,
-                        line + 1, column + 1, ExtractLine(text_proto_, line));
+                        line + 1, column + 1, ExtractLine(line));
 }
 
 void ErrorCollector::RecordWarning(int line, google::protobuf::io::ColumnNumber column,
                                    absl::string_view message) {
   LOG(WARNING) << message << " [line " << line + 1 << ", column " << column + 1
                << "]\n"
-               << ExtractLine(text_proto_, line);
+               << ExtractLine(line);
 }
 
 }  // namespace internal
diff --git a/parse_text_proto.h b/parse_text_proto.h
index 3ea906e..d080718 100644
--- a/parse_text_proto.h
+++ b/parse_text_proto.h
@@ -1,22 +1,30 @@
 #ifndef PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_PARSE_TEXT_PROTO_H_
 #define PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_PARSE_TEXT_PROTO_H_
 
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+#include <optional>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "absl/log/log.h"
 #include "absl/status/status.h"
 #include "absl/status/statusor.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "google/protobuf/io/tokenizer.h"
+#include "google/protobuf/io/zero_copy_stream_impl.h"
 #include "google/protobuf/text_format.h"
 
 namespace safepower_agent {
 namespace internal {
 class ErrorCollector : public google::protobuf::io::ErrorCollector {
  public:
-  explicit ErrorCollector(absl::string_view text_proto)
-      : text_proto_(text_proto) {}
+  ErrorCollector() = default;
   ErrorCollector(const ErrorCollector&) = delete;
   ErrorCollector& operator=(const ErrorCollector&) = delete;
 
@@ -26,16 +34,38 @@
                      absl::string_view message) override;
   std::string message() && { return std::move(message_); }
 
+ protected:
+  virtual std::string ExtractLine(int line) const = 0;
+
  private:
-  absl::string_view text_proto_;
   std::string message_;
 };
+
+class ErrorCollectorString : public ErrorCollector {
+ public:
+  explicit ErrorCollectorString(absl::string_view text_proto)
+      : text_proto_(text_proto) {}
+  std::string ExtractLine(int line) const override;
+
+ private:
+  absl::string_view text_proto_;
+};
+
+class ErrorCollectorFile : public ErrorCollector {
+ public:
+  explicit ErrorCollectorFile(const std::string& path) : path_(path) {}
+  std::string ExtractLine(int line) const override;
+
+ private:
+  const std::string& path_;
+};
+
 }  // namespace internal
 
 template <typename Proto>
 absl::StatusOr<Proto> ParseTextProto(absl::string_view text_proto) {
   google::protobuf::TextFormat::Parser parser;
-  internal::ErrorCollector error_collector(text_proto);
+  internal::ErrorCollectorString error_collector(text_proto);
   parser.RecordErrorsTo(&error_collector);
   Proto proto;
   if (!parser.ParseFromString(text_proto, &proto)) {
@@ -43,6 +73,26 @@
   }
   return proto;
 }
+
+template <typename Proto>
+absl::StatusOr<Proto> ParseTextProtoFromFile(const std::string& path) {
+  google::protobuf::TextFormat::Parser parser;
+  internal::ErrorCollectorFile error_collector(path);
+  parser.RecordErrorsTo(&error_collector);
+  Proto proto;
+  int fd = open(path.c_str(), O_RDONLY);
+  if (fd < 0) {
+    return absl::InvalidArgumentError(
+        absl::StrCat("Failed to open file: ", path, " :", strerror(errno)));
+  }
+  google::protobuf::io::FileInputStream stream(fd);
+  stream.SetCloseOnDelete(true);
+  if (!parser.Parse(&stream, &proto)) {
+    return absl::InvalidArgumentError(std::move(error_collector).message());
+  }
+  return proto;
+}
+
 }  // namespace safepower_agent
 
 #endif  // PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_PARSE_TEXT_PROTO_H_