Implement streaming for IR structs and enums so that the error messages are non-opaque.

PiperOrigin-RevId: 400951273
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index e525bdf..a85685a 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -97,6 +97,10 @@
   }
 }
 
+std::ostream& operator<<(std::ostream& o, const AccessSpecifier& access) {
+  return o << AccessToString(access);
+}
+
 nlohmann::json Field::ToJson() const {
   nlohmann::json result;
   result["type"] = type.ToJson();
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index 8c677ee..08663f8 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -43,6 +43,10 @@
   std::string name_;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const HeaderName& h) {
+  return o << h.ToJson();
+}
+
 // A C++ type involved in the bindings. It has the knowledge of how the type
 // is spelled in C++.
 struct CcType {
@@ -80,6 +84,10 @@
   std::vector<RsType> type_params = {};
 };
 
+inline std::ostream& operator<<(std::ostream& o, const RsType& type) {
+  return o << type.ToJson();
+}
+
 // A type involved in the bindings. The rs_type and cc_type will be treated
 // as interchangeable during bindings, and so should share the same layout.
 //
@@ -114,6 +122,10 @@
   CcType cc_type;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const MappedType& type) {
+  return o << type.ToJson();
+}
+
 // An identifier involved in bindings.
 //
 // Examples:
@@ -137,6 +149,10 @@
   std::string identifier_;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const Identifier& id) {
+  return o << id.Ident();
+}
+
 // A function parameter.
 //
 // Examples:
@@ -149,6 +165,10 @@
   Identifier identifier;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const FuncParam& param) {
+  return o << param.ToJson();
+}
+
 // A function involved in the bindings.
 struct Func {
   nlohmann::json ToJson() const;
@@ -160,6 +180,10 @@
   bool is_inline;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const Func& f) {
+  return o << f.ToJson();
+}
+
 // Access specifier for a member or base class.
 enum AccessSpecifier {
   kPublic,
@@ -167,6 +191,8 @@
   kPrivate,
 };
 
+std::ostream& operator<<(std::ostream& o, const AccessSpecifier& access);
+
 // A field (non-static member variable) of a record.
 struct Field {
   nlohmann::json ToJson() const;
@@ -178,6 +204,10 @@
   uint64_t offset;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const Field& f) {
+  return o << f.ToJson();
+}
+
 // A record (struct, class, union).
 struct Record {
   nlohmann::json ToJson() const;
@@ -199,6 +229,10 @@
   bool is_trivial_abi = false;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const Record& r) {
+  return o << r.ToJson();
+}
+
 // A complete intermediate representation of bindings for publicly accessible
 // declarations of a single C++ library.
 struct IR {
@@ -210,6 +244,10 @@
   std::vector<std::variant<Func, Record>> items;
 };
 
+inline std::ostream& operator<<(std::ostream& o, const IR& ir) {
+  return o << ir.ToJson();
+}
+
 }  // namespace rs_bindings_from_cc
 
 #endif  // CRUBIT_RS_BINDINGS_FROM_CC_IR_H_