#include #include #include #include #include #include #include "cpp_styleguide.h" #include namespace NProtobuf { namespace NCompiler { namespace NPlugins { using namespace google::protobuf; using namespace google::protobuf::compiler; using namespace google::protobuf::compiler::cpp; typedef std::map TVariables; bool GenerateYaStyle(const FileDescriptor* fileDescriptor) { const auto& extension = fileDescriptor->FindExtensionByName("GenerateYaStyle"); return extension; } bool GenerateYaStyle(const FieldDescriptor* descriptor) { const auto& fileDescriptor = descriptor->file(); return GenerateYaStyle(fileDescriptor); } void SetCommonFieldVariables(const FieldDescriptor* descriptor, TVariables* variables) { const auto& name = descriptor->name(); if (GenerateYaStyle(descriptor)) (*variables)["rname"] = UnderscoresToCamelCase(name, true); else (*variables)["rname"] = name; (*variables)["name"] = FieldName(descriptor); } TProtoStringType HeaderFileName(const FileDescriptor* file) { TProtoStringType basename = compiler::StripProto(file->name()); bool use_proto_h = !!getenv("PROTOC_PLUGINS_LITE_HEADERS"); return use_proto_h ? basename.append(".proto.h") : basename.append(".pb.h"); } TProtoStringType SourceFileName(const FileDescriptor* file) { TProtoStringType basename = compiler::StripProto(file->name()); return basename.append(".pb.cc"); } bool IsLiteRuntimeMessage(const Descriptor* desc) { return desc->file() != NULL && desc->file()->options().optimize_for() == google::protobuf::FileOptions::LITE_RUNTIME; } bool IsAutogeneratedNestedType(const Descriptor* desc) { return desc->options().map_entry(); } class TFieldExtGenerator { public: TFieldExtGenerator(const FieldDescriptor* field) : Field_(field) { SetCommonFieldVariables(Field_, &Variables_); } virtual ~TFieldExtGenerator() { } virtual void GenerateAccessorDeclarations(io::Printer* printer) = 0; virtual void GenerateJSONPrinting(io::Printer* printer) = 0; protected: void GenerateRepeatedJSONPrinting(io::Printer* printer, const char* itemPrinter) { printer->Print("out << '[';\n"); printer->Print("{\n"); printer->Indent(); printer->Print("const char* separator = \"\";\n"); printer->Print(Variables_, "for (size_t _index = 0; _index < $rname$Size(); ++_index) {\n"); printer->Indent(); printer->Print("out << separator;\n"); printer->Print(Variables_, itemPrinter); printer->Print(";\n"); printer->Print("separator = \",\";\n"); printer->Outdent(); printer->Print("}\n"); printer->Outdent(); printer->Print("}\n"); printer->Print("out << ']';\n"); } protected: const FieldDescriptor* Field_; TVariables Variables_; }; class TMessageFieldExtGenerator: public TFieldExtGenerator { public: TMessageFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["type"] = QualifiedClassName(Field_->message_type()); printer->Print(Variables_, "inline const $type$& Get$rname$() const { return $name$(); }\n" "inline $type$* Mutable$rname$() { return mutable_$name$(); }\n"); } void GenerateJSONPrinting(io::Printer* printer) override { printer->Print(Variables_, "Get$rname$().PrintJSON(out);\n"); } }; class TMapFieldExtGenerator: public TFieldExtGenerator { public: TMapFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) , Key_(field->message_type()->FindFieldByName("key")) , Val_(field->message_type()->FindFieldByName("value")) { Variables_["key_cpp"] = PrimitiveTypeName(Key_->cpp_type()); switch (Val_->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: Variables_["val_cpp"] = QualifiedClassName(Val_->message_type()); break; case FieldDescriptor::CPPTYPE_ENUM: Variables_["val_cpp"] = ClassName(Val_->enum_type(), true); break; default: Variables_["val_cpp"] = PrimitiveTypeName(Val_->cpp_type()); } } void GenerateAccessorDeclarations(io::Printer* printer) { printer->Print(Variables_, "inline const ::google::protobuf::Map<$key_cpp$, $val_cpp$>& Get$rname$() const { return $name$(); }\n" "inline ::google::protobuf::Map<$key_cpp$, $val_cpp$>* Mutable$rname$() { return mutable_$name$(); }\n"); } void GenerateKeyValuePrinting(io::Printer* printer, const char* scopeName, bool isKey) { const FieldDescriptor* desc = isKey ? Key_ : Val_; switch(desc->cpp_type()) { case FieldDescriptor::CPPTYPE_STRING: printer->Print(TString::Join("::google::protobuf::io::PrintJSONString(out, ", scopeName , ");\n").data()); break; case FieldDescriptor::CPPTYPE_ENUM: printer->Print(TString::Join("out << int(", scopeName, ");\n").data()); break; case FieldDescriptor::CPPTYPE_MESSAGE: printer->Print(TString::Join(scopeName, ".PrintJSON(out);\n").data()); break; default: if (isKey) { printer->Print(TString::Join("out << '\"' << ", scopeName, " << '\"';\n").data()); } else { printer->Print(TString::Join("out << ", scopeName, ";\n").data()); } } } void GenerateJSONPrinting(io::Printer* printer) { printer->Print(Variables_, "out << '{';\n" "const ::google::protobuf::Map<$key_cpp$, $val_cpp$>& map = Get$rname$();\n" "for (auto it = map.begin(); it != map.end(); ++it) {\n" ); printer->Indent(); printer->Print("if (it != map.begin()) { out << ','; }\n"); GenerateKeyValuePrinting(printer, "it->first", true); printer->Print("out << ':';\n"); GenerateKeyValuePrinting(printer, "it->second", false); printer->Outdent(); printer->Print("}\n"); printer->Print("out << '}';\n"); } private: const FieldDescriptor* Key_; const FieldDescriptor* Val_; }; class TRepeatedMessageFieldExtGenerator: public TFieldExtGenerator { public: TRepeatedMessageFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["type"] = QualifiedClassName(Field_->message_type()); printer->Print(Variables_, "inline const $type$& Get$rname$(size_t _index) const {Y_ASSERT(_index < static_cast(::Max())); return $name$(int(_index)); }\n" "inline $type$* Mutable$rname$(size_t _index) {Y_ASSERT(_index < static_cast(::Max())); return mutable_$name$(int(_index)); }\n" "inline $type$* Add$rname$() { return add_$name$(); }\n" "inline const $type$& get_idx_$name$(int _index) const { return $name$(_index); }\n" "inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n" " get_arr_$name$() const { return $name$(); }\n" "inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n" " Get$rname$() const { return $name$(); }\n" "inline ::google::protobuf::RepeatedPtrField< $type$ >*\n" " Mutable$rname$() { return mutable_$name$(); }\n"); } void GenerateJSONPrinting(io::Printer* printer) override { GenerateRepeatedJSONPrinting(printer, "Get$rname$(_index).PrintJSON(out)"); } }; class TStringFieldExtGenerator: public TFieldExtGenerator { public: TStringFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["pointer_type"] = Field_->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; if (Field_->options().ctype() != FieldOptions::STRING) { printer->Outdent(); printer->Print( " private:\n" " // Hidden due to unknown ctype option.\n"); printer->Indent(); } printer->Print(Variables_, "inline const TProtoStringType& Get$rname$() const { return $name$(); }\n" "inline void Set$rname$(const TProtoStringType& value) { set_$name$(value); }\n" "inline void Set$rname$(TProtoStringType&& value) { set_$name$(std::move(value)); }\n" "inline void Set$rname$(const char* value) { set_$name$(value); }\n" "inline void Set$rname$(const $pointer_type$* value, size_t size) { set_$name$(value, size); }\n" "inline TProtoStringType* Mutable$rname$() { return mutable_$name$(); }\n"); if (Field_->options().ctype() != FieldOptions::STRING) { printer->Outdent(); printer->Print(" public:\n"); printer->Indent(); } } void GenerateJSONPrinting(io::Printer* printer) override { printer->Print(Variables_, "::google::protobuf::io::PrintJSONString(out, Get$rname$());\n"); } }; class TRepeatedStringFieldExtGenerator: public TFieldExtGenerator { public: TRepeatedStringFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["pointer_type"] = Field_->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; if (Field_->options().ctype() != FieldOptions::STRING) { printer->Outdent(); printer->Print( " private:\n" " // Hidden due to unknown ctype option.\n"); printer->Indent(); } printer->Print(Variables_, "inline const TProtoStringType& Get$rname$(size_t _index) const {Y_ASSERT(_index < static_cast(::Max())); return $name$(_index); }\n" "inline TProtoStringType* Mutable$rname$(size_t _index) {Y_ASSERT(_index < static_cast(::Max())); return mutable_$name$(_index); }\n" "inline void Set$rname$(size_t _index, const TProtoStringType& value) {Y_ASSERT(_index < static_cast(::Max())); set_$name$(_index, value); }\n" "inline void Set$rname$(size_t _index, TProtoStringType&& value) {Y_ASSERT(_index < static_cast(::Max())); set_$name$(_index, std::move(value)); }\n" "inline void Set$rname$(size_t _index, const char* value) {Y_ASSERT(_index < static_cast(::Max())); set_$name$(_index, value); }\n" "inline void Set$rname$(size_t _index, const $pointer_type$* value, size_t size) {Y_ASSERT(_index < static_cast(::Max())); set_$name$(_index, value, size); }\n" "inline TProtoStringType* Add$rname$() { return add_$name$(); }\n" "inline void Add$rname$(const TProtoStringType& value) { add_$name$(value); }\n" "inline void Add$rname$(TProtoStringType&& value) { add_$name$(std::move(value)); }\n" "inline void Add$rname$(const char* value) { add_$name$(value); }\n" "inline void Add$rname$(const $pointer_type$* value, size_t size) { add_$name$(value, size); }\n" "inline const TProtoStringType& get_idx_$name$(int _index) const { return $name$(_index); }\n" "inline const ::google::protobuf::RepeatedPtrField& get_arr_$name$() const" "{ return $name$(); }\n" "inline const ::google::protobuf::RepeatedPtrField& Get$rname$() const" "{ return $name$(); }\n" "inline ::google::protobuf::RepeatedPtrField* Mutable$rname$()" "{ return mutable_$name$(); }\n"); if (Field_->options().ctype() != FieldOptions::STRING) { printer->Outdent(); printer->Print(" public:\n"); printer->Indent(); } } void GenerateJSONPrinting(io::Printer* printer) override { GenerateRepeatedJSONPrinting( printer, "::google::protobuf::io::PrintJSONString(out, Get$rname$(_index))" ); } }; class TEnumFieldExtGenerator: public TFieldExtGenerator { public: TEnumFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["type"] = ClassName(Field_->enum_type(), true); printer->Print(Variables_, "inline $type$ Get$rname$() const { return $name$(); }\n" "inline void Set$rname$($type$ value) { set_$name$(value); }\n"); } void GenerateJSONPrinting(io::Printer* printer) override { printer->Print(Variables_, "out << (int)Get$rname$();\n"); } }; class TRepeatedEnumFieldExtGenerator: public TFieldExtGenerator { public: TRepeatedEnumFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["type"] = ClassName(Field_->enum_type(), true); printer->Print(Variables_, "inline $type$ Get$rname$(size_t _index) const {Y_ASSERT(_index < static_cast(::Max())); return $name$(_index); }\n" "inline void Set$rname$(size_t _index, $type$ value) {Y_ASSERT(_index < static_cast(::Max())); set_$name$(_index, value); }\n" "inline void Add$rname$($type$ value) { add_$name$(value); }\n" "inline $type$ get_idx_$name$(int _index) const {return $name$(_index); }\n" "inline const ::google::protobuf::RepeatedField& get_arr_$name$() const { return $name$(); }\n" "inline const ::google::protobuf::RepeatedField& Get$rname$() const { return $name$(); }\n" "inline ::google::protobuf::RepeatedField* Mutable$rname$() { return mutable_$name$(); }\n"); } void GenerateJSONPrinting(io::Printer* printer) override { GenerateRepeatedJSONPrinting(printer, "out << (int)Get$rname$(_index)"); } }; class TPrimitiveFieldExtGenerator: public TFieldExtGenerator { public: TPrimitiveFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["type"] = PrimitiveTypeName(Field_->cpp_type()); printer->Print(Variables_, "inline $type$ Get$rname$() const { return $name$();}\n" "inline void Set$rname$($type$ value) { set_$name$(value); }\n"); } void GenerateJSONPrinting(io::Printer* printer) override { printer->Print(Variables_, "out << Get$rname$();\n"); } }; class TRepeatedPrimitiveFieldExtGenerator: public TFieldExtGenerator { public: TRepeatedPrimitiveFieldExtGenerator(const FieldDescriptor* field) : TFieldExtGenerator(field) { } void GenerateAccessorDeclarations(io::Printer* printer) override { Variables_["type"] = PrimitiveTypeName(Field_->cpp_type()); printer->Print(Variables_, "inline $type$ Get$rname$(size_t _index) const {Y_ASSERT(_index < static_cast(::Max())); return $name$(_index); }\n" "inline void Set$rname$(size_t _index, $type$ value) {Y_ASSERT(_index < static_cast(::Max())); set_$name$(_index, value); }\n" "inline void Add$rname$($type$ value) { add_$name$(value); }\n" "inline $type$ get_idx_$name$(int _index) const { return $name$(_index); }\n" "inline const ::google::protobuf::RepeatedField< $type$ >&\n" " get_arr_$name$() const { return $name$(); }\n" "inline const ::google::protobuf::RepeatedField< $type$ >&\n" " Get$rname$() const { return $name$(); }\n" "inline ::google::protobuf::RepeatedField< $type$ >*\n" " Mutable$rname$() { return mutable_$name$(); }\n"); } void GenerateJSONPrinting(io::Printer* printer) override { GenerateRepeatedJSONPrinting(printer, "out << Get$rname$(_index)"); } }; class TBoolFieldExtGenerator: public TPrimitiveFieldExtGenerator { public: TBoolFieldExtGenerator(const FieldDescriptor* field) : TPrimitiveFieldExtGenerator(field) { } void GenerateJSONPrinting(io::Printer* printer) override { printer->Print(Variables_, "out << (Get$rname$() ? \"true\" : \"false\");\n"); } }; class TRepeatedBoolFieldExtGenerator: public TRepeatedPrimitiveFieldExtGenerator { public: TRepeatedBoolFieldExtGenerator(const FieldDescriptor* field) : TRepeatedPrimitiveFieldExtGenerator(field) { } void GenerateJSONPrinting(io::Printer* printer) override { GenerateRepeatedJSONPrinting(printer, "out << (Get$rname$(_index) ? \"true\" : \"false\")"); } }; class TFloatFieldExtGenerator: public TPrimitiveFieldExtGenerator { public: TFloatFieldExtGenerator(const FieldDescriptor* field) : TPrimitiveFieldExtGenerator(field) { } void GenerateJSONPrinting(io::Printer* printer) override { printer->Print(Variables_, "out << double(Get$rname$());\n"); } }; class TRepeatedFloatFieldExtGenerator: public TRepeatedPrimitiveFieldExtGenerator { public: TRepeatedFloatFieldExtGenerator(const FieldDescriptor* field) : TRepeatedPrimitiveFieldExtGenerator(field) { } void GenerateJSONPrinting(io::Printer* printer) override { GenerateRepeatedJSONPrinting(printer, "out << double(Get$rname$(_index))"); } }; // borrowed mostly from protobuf/compiler/cpp/cpp_extension.cc class TExtensionGenerator { public: TExtensionGenerator(const FieldDescriptor* descriptor) : Descriptor_(descriptor) { if (Descriptor_->is_repeated()) { type_traits_ = "Repeated"; } TProtoStringType clsName; switch (Descriptor_->cpp_type()) { case FieldDescriptor::CPPTYPE_ENUM: type_traits_.append("EnumTypeTraits< "); clsName = ClassName(Descriptor_->enum_type(), true); type_traits_.append(clsName); type_traits_.append(", "); type_traits_.append(clsName); type_traits_.append("_IsValid>"); break; case FieldDescriptor::CPPTYPE_STRING: type_traits_.append("StringTypeTraits"); break; case FieldDescriptor::CPPTYPE_MESSAGE: type_traits_.append("MessageTypeTraits< "); type_traits_.append(ClassName(Descriptor_->message_type(), true)); type_traits_.append(" >"); break; default: type_traits_.append("PrimitiveTypeTraits< "); type_traits_.append(PrimitiveTypeName(Descriptor_->cpp_type())); type_traits_.append(" >"); break; } } void GenerateDeclaration(io::Printer* printer) const { TVariables vars; vars["extendee" ] = ClassName(Descriptor_->containing_type(), true); vars["type_traits" ] = type_traits_; vars["name" ] = Descriptor_->name(); vars["field_type" ] = std::to_string(static_cast(Descriptor_->type())); vars["packed" ] = Descriptor_->options().packed() ? "true" : "false"; printer->Print(vars, "typedef ::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" " ::google::protobuf::internal::$type_traits$, $field_type$, $packed$ >\n" " Td$name$;\n" ); } private: const FieldDescriptor* Descriptor_; TProtoStringType type_traits_; }; class TOneofGenerator { public: TOneofGenerator(const OneofDescriptor* Descriptor_) : Descriptor_(Descriptor_) { Variables_["camel_oneof_name"] = UnderscoresToCamelCase(Descriptor_->name(), true); Variables_["rname"] = Descriptor_->name(); } void GenerateDeclarations(io::Printer* printer) const { printer->Print(Variables_, "$camel_oneof_name$Case Get$rname$Case() const { return $rname$_case(); }\n"); printer->Print(Variables_, "void Clear$rname$() { clear_$rname$(); }\n"); if (Descriptor_->name() != UnderscoresToCamelCase(Descriptor_->name(), true)) { printer->Print(Variables_, "$camel_oneof_name$Case Get$camel_oneof_name$Case() const { return $rname$_case(); }\n"); printer->Print(Variables_, "void Clear$camel_oneof_name$() { clear_$rname$(); }\n"); } } private: const OneofDescriptor* Descriptor_; TVariables Variables_; }; TFieldExtGenerator* MakeGenerator(const FieldDescriptor* field) { if (field->is_repeated()) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: if (field->is_map()) { return new TMapFieldExtGenerator(field); } return new TRepeatedMessageFieldExtGenerator(field); case FieldDescriptor::CPPTYPE_BOOL: return new TRepeatedBoolFieldExtGenerator(field); case FieldDescriptor::CPPTYPE_FLOAT: return new TRepeatedFloatFieldExtGenerator(field); case FieldDescriptor::CPPTYPE_STRING: switch (field->options().ctype()) { default: // RepeatedStringFieldExtGenerator handles unknown ctypes. case FieldOptions::STRING: return new TRepeatedStringFieldExtGenerator(field); } case FieldDescriptor::CPPTYPE_ENUM: return new TRepeatedEnumFieldExtGenerator(field); default: return new TRepeatedPrimitiveFieldExtGenerator(field); } } else { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: return new TMessageFieldExtGenerator(field); case FieldDescriptor::CPPTYPE_BOOL: return new TBoolFieldExtGenerator(field); case FieldDescriptor::CPPTYPE_FLOAT: return new TFloatFieldExtGenerator(field); case FieldDescriptor::CPPTYPE_STRING: switch (field->options().ctype()) { default: // StringFieldGenerator handles unknown ctypes. case FieldOptions::STRING: return new TStringFieldExtGenerator(field); } case FieldDescriptor::CPPTYPE_ENUM: return new TEnumFieldExtGenerator(field); default: return new TPrimitiveFieldExtGenerator(field); } } } class TMessageExtGenerator { public: TMessageExtGenerator(const Descriptor* descriptor, OutputDirectory* outputDirectory) : Descriptor_(descriptor) , Classname_(ClassName(descriptor, false)) , OutputDirectory_(outputDirectory) { for (int i = 0; i < descriptor->nested_type_count(); i++) { if (!IsAutogeneratedNestedType(descriptor->nested_type(i))) { NestedGenerators_.emplace_back(descriptor->nested_type(i), OutputDirectory_); } } FieldGenerators_.reserve(descriptor->field_count()); for (int i = 0; i < descriptor->field_count(); i++) { FieldGenerators_.emplace_back(MakeGenerator(descriptor->field(i))); } ExtensionGenerators_.reserve(descriptor->extension_count()); for (int i = 0; i < descriptor->extension_count(); i++) { ExtensionGenerators_.emplace_back(descriptor->extension(i)); } OneofGenerators_.reserve(descriptor->real_oneof_decl_count()); for (int i = 0; i < descriptor->real_oneof_decl_count(); i++) { OneofGenerators_.emplace_back(descriptor->oneof_decl(i)); } } void GenerateClassDefinitionExtension() { GenerateSaveLoadImplementation(); GenerateJSONImplementation(); for (auto& nestedGenerator: NestedGenerators_) { nestedGenerator.GenerateClassDefinitionExtension(); } } void GenerateDebugOutputExtension() { GenerateDebugOutput(); for (auto& nestedGenerator: NestedGenerators_) { nestedGenerator.GenerateDebugOutputExtension(); } } void GenerateTypedefOutputExtension(bool nested) { GenerateTypedefOutput(nested); for (auto& nestedGenerator: NestedGenerators_) { nestedGenerator.GenerateTypedefOutputExtension(true); } } void GenerateClassExtension() { GenerateDebugStringImplementation(); for (auto& nestedGenerator: NestedGenerators_) { nestedGenerator.GenerateClassExtension(); } } void GenerateDeclarations() { GenerateFieldAccessorDeclarations(); for (auto& nestedGenerator: NestedGenerators_) { nestedGenerator.GenerateDeclarations(); } } void GenerateDefinitions() { GenerateClassExtension(); GenerateDebugOutputExtension(); GenerateClassDefinitionExtension(); } private: void GenerateFieldAccessorDeclarations() { TProtoStringType fileName = HeaderFileName(Descriptor_->file()); TProtoStringType scope = "class_scope:" + Descriptor_->full_name(); std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); printer.Print("// Yandex cpp-styleguide extension\n"); for (int i = 0; i < Descriptor_->field_count(); i++) { const FieldDescriptor* field = Descriptor_->field(i); TVariables vars; SetCommonFieldVariables(field, &vars); if (field->is_repeated()) { printer.Print(vars, "inline size_t $rname$Size() const { return (size_t)$name$_size(); }\n"); } else if (field->has_presence()) { printer.Print(vars, "inline bool Has$rname$() const { return has_$name$(); }\n"); } printer.Print(vars, "inline void Clear$rname$() { clear_$name$(); }\n"); // Generate type-specific accessor declarations. FieldGenerators_[i]->GenerateAccessorDeclarations(&printer); printer.Print("\n"); } for (auto& extensionGenerator: ExtensionGenerators_) { extensionGenerator.GenerateDeclaration(&printer); } for (auto& oneofGenerator: OneofGenerators_) { oneofGenerator.GenerateDeclarations(&printer); } TVariables vars; vars["class"] = ClassName(Descriptor_, false); if (!IsLiteRuntimeMessage(Descriptor_)) { printer.Print("TProtoStringType ShortUtf8DebugString() const;\n"); printer.Print("void PrintJSON(IOutputStream&) const override;\n"); printer.Print(vars, "::google::protobuf::io::TAsJSON<$class$> AsJSON() const {\n"); printer.Print(vars, " return ::google::protobuf::io::TAsJSON<$class$>(*this);\n"); printer.Print("}\n"); printer.Print("void Save(IOutputStream* output) const;\n"); printer.Print("void Load(IInputStream* input);\n"); } printer.Print("// End of Yandex-specific extension\n"); } void GenerateSaveLoadImplementation() { TProtoStringType fileName = SourceFileName(Descriptor_->file()); TProtoStringType scope = "namespace_scope"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); TVariables vars; vars["class"] = Classname_; if (!IsLiteRuntimeMessage(Descriptor_)) { printer.Print("// Yandex-specific extension\n"); printer.Print(vars, "void $class$::Save(IOutputStream* output) const {\n"); printer.Print(" ::Save(output, static_cast(*this));\n"); printer.Print("}\n"); printer.Print(vars, "void $class$::Load(IInputStream* input) {\n"); printer.Print(" ::Load(input, static_cast<::google::protobuf::Message&>(*this));\n"); printer.Print("}\n"); printer.Print("// End of Yandex-specific extension\n"); } } void GenerateDebugStringImplementation() { TProtoStringType fileName = SourceFileName(Descriptor_->file()); TProtoStringType scope = "namespace_scope"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); TVariables vars; vars["class"] = Classname_; if (!IsLiteRuntimeMessage(Descriptor_)) { printer.Print("// Yandex-specific extension\n"); printer.Print(vars, "TProtoStringType $class$::ShortUtf8DebugString() const {\n"); printer.Print(" return ::ShortUtf8DebugString(*this);\n"); printer.Print("}\n"); printer.Print("// End of Yandex-specific extension\n"); } } void GenerateJSONImplementation() { if (IsLiteRuntimeMessage(Descriptor_)) { return; } TProtoStringType fileName = SourceFileName(Descriptor_->file()); TProtoStringType scope = "namespace_scope"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); printer.Print("// Yandex JSON extension\n"); TVariables vars; vars["class"] = ClassName(Descriptor_, true); printer.Print(vars, "inline void $class$::PrintJSON(IOutputStream& out) const {\n"); printer.Indent(); printer.Print("out << '{';\n"); if (Descriptor_->field_count() > 0) { printer.Print("const char* sep = \"\";\n"); } for (int i = 0; i < Descriptor_->field_count(); i++) { const FieldDescriptor* field = Descriptor_->field(i); TVariables vars; SetCommonFieldVariables(field, &vars); if (field->is_repeated()) { // map or repeated field in both proto3 and proto2 syntax printer.Print(vars, "if ($rname$Size() > 0) {\n"); } else if (field->has_presence()) { // any optional or required field in proto2 syntax // message-field or any oneof field in proto3 syntax printer.Print(vars, "if (Has$rname$()) {\n"); } else { // string, enum or primitive field in proto3 syntax printer.Print(vars, "if (Get$rname$()) {\n"); } printer.Indent(); printer.Print("out << sep;\n"); printer.Print(vars, "out << \"\\\"$rname$\\\":\";\n"); FieldGenerators_[i]->GenerateJSONPrinting(&printer); printer.Print(vars, "sep = \",\";\n"); printer.Outdent(); printer.Print("}\n"); } printer.Print("out << '}';\n"); printer.Outdent(); printer.Print("}\n"); printer.Print("// End of Yandex JSON extension\n"); } void GenerateDebugOutput() { TProtoStringType fileName = SourceFileName(Descriptor_->file()); TProtoStringType scope = "global_scope"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); if (!IsLiteRuntimeMessage(Descriptor_)) { printer.Print("// Yandex debug output extension\n"); TVariables vars; vars["class"] = ClassName(Descriptor_, true); printer.Print("template<>\n"); printer.Print(vars, "void Out< $class$>(IOutputStream& out, const $class$& msg) {\n"); printer.Print(" out << \"{ \" << msg.ShortUtf8DebugString() << \" }\";\n"); printer.Print("}\n"); printer.Print("// End of Yandex debug output extension\n"); } } void GenerateTypedefOutput(bool nested) { if (!GenerateYaStyle(Descriptor_->file())) return; TProtoStringType fileName = HeaderFileName(Descriptor_->file()); TProtoStringType scope = nested ? "class_scope:" + Descriptor_->full_name().substr(0, Descriptor_->full_name().size() - Descriptor_->name().size() - 1) : "namespace_scope"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); TString name = Descriptor_->name(); bool isOk = name.size() >= 2 && name[0] == 'T' && name[1] >= 'A' && name[1] <= 'Z'; if (!isOk) { printer.Print("// Yandex typedef extension\n"); TVariables vars; vars["class"] = name; vars["base_class"] = ClassName(Descriptor_, true); printer.Print(vars, "typedef $base_class$ T$class$;\n"); printer.Print("// End of Yandex typedef extension\n"); } } private: const Descriptor* Descriptor_; TProtoStringType Classname_; OutputDirectory* OutputDirectory_; std::vector> FieldGenerators_; std::vector NestedGenerators_; std::vector ExtensionGenerators_; std::vector OneofGenerators_; }; class TFileExtGenerator { public: TFileExtGenerator(const FileDescriptor* file, OutputDirectory* output_directory) : File_(file) , OutputDirectory_(output_directory) { MessageGenerators_.reserve(file->message_type_count()); for (int i = 0; i < file->message_type_count(); i++) { MessageGenerators_.emplace_back(file->message_type(i), OutputDirectory_); } } void GenerateHeaderExtensions() { GenerateHeaderIncludeExtensions(); for (auto& messageGenerator: MessageGenerators_) { messageGenerator.GenerateTypedefOutputExtension(false); messageGenerator.GenerateDeclarations(); } } void GenerateSourceExtensions() { GenerateSourceIncludeExtensions(); for (auto& messageGenerator: MessageGenerators_) { messageGenerator.GenerateDefinitions(); } } private: void GenerateSourceIncludeExtensions() { TProtoStringType fileName = SourceFileName(File_); TProtoStringType scope = "includes"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); printer.Print("#include \n"); } void GenerateHeaderIncludeExtensions() { TProtoStringType fileName = HeaderFileName(File_); TProtoStringType scope = "includes"; std::unique_ptr output( OutputDirectory_->OpenForInsert(fileName, scope)); io::Printer printer(output.get(), '$'); printer.Print("#include \n"); } private: const FileDescriptor* File_; OutputDirectory* OutputDirectory_; size_t MessageTypeCount_; std::vector MessageGenerators_; }; bool TCppStyleGuideExtensionGenerator::Generate(const FileDescriptor* file, const TProtoStringType&, OutputDirectory* outputDirectory, TProtoStringType*) const { TFileExtGenerator fileGenerator(file, outputDirectory); // Generate header. fileGenerator.GenerateHeaderExtensions(); // Generate cc file. fileGenerator.GenerateSourceExtensions(); return true; } } } } int main(int argc, char* argv[]) { #ifdef _MSC_VER // Don't print a silly message or stick a modal dialog box in my face, // please. _set_abort_behavior(0, ~0); #endif // !_MSC_VER NProtobuf::NCompiler::NPlugins::TCppStyleGuideExtensionGenerator generator; return google::protobuf::compiler::PluginMain(argc, argv, &generator); }