diff --git a/proto/compiler/google/protobuf/compiler/kotlin/Makefile b/proto/compiler/google/protobuf/compiler/kotlin/Makefile new file mode 100644 index 00000000000..acfcc7ee630 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/Makefile @@ -0,0 +1,29 @@ +CXX = g++ +CXXFLAGS = -O2 -std=c++11 +LDFLAGS = -lprotoc -lprotobuf + +EXE = protoc +SRCDIR = src +BINDIR = bin + +OBJECTS = $(patsubst $(SRCDIR)/%.cc,$(BINDIR)/%.o,$(wildcard $(SRCDIR)/*.cc)) + +all: $(EXE) + +$(EXE): $(BINDIR) $(OBJECTS) + $(CXX) $(OBJECTS) -o $(EXE) $(LDFLAGS) + +$(BINDIR)/%.o: $(SRCDIR)/%.cc + $(CXX) $(CXXFLAGS) -c -MMD -o $@ $< -lprotoc + +include $(wildcard $(BINDIR)/*.d) + +$(BINDIR): + mkdir -p $(BINDIR) + +clean: + rm -rf $(BINDIR) $(EXE) + +generate: + ./protoc --kotlin_out=./ ./test/addressbook.proto +.PHONY: clean all generate \ No newline at end of file diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_builder_generator.cpp b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_builder_generator.cpp new file mode 100644 index 00000000000..2d76c990c88 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_builder_generator.cpp @@ -0,0 +1,22 @@ +// +// Created by user on 7/12/16. +// + +#include "kotlin_builder_generator.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +BuilderGenerator::BuilderGenerator(const google::protobuf::Descriptor * descriptor) { + +} + +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google + + + diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_builder_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_builder_generator.h new file mode 100644 index 00000000000..332501db72c --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_builder_generator.h @@ -0,0 +1,33 @@ +// +// Created by user on 7/12/16. +// + +#ifndef PROTOBUF_KOTLIN_BUILDER_GENERATOR_H +#define PROTOBUF_KOTLIN_BUILDER_GENERATOR_H + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +} +class BuilderGenerator { +public: + string simpleName; + vector < + + BuilderGenerator(Descriptor const * descriptor); + ~BuilderGenerator(); + void generateCode(io::Printer * printer); +}; + +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google + + +#endif //PROTOBUF_KOTLIN_BUILDER_GENERATOR_H diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc new file mode 100644 index 00000000000..3d6606cc4df --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc @@ -0,0 +1,175 @@ +// +// Created by user on 7/11/16. +// + +#include "kotlin_class_generator.h" +#include +#include "kotlin_enum_generator.h" +#include "kotlin_field_generator.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { + // print class header + map vars; + vars["modifier"] = modifier.getName(); + vars["name"] = (isBuilder? "Builder" : "") + simpleName; + printer->Print(vars, + "$modifier$ $name$ private constructor () {" + "\n" + ); + printer->Indent(); + + // generate code for nested classes declarations + for (ClassGenerator *gen: classesDeclarations) { + gen->generateCode(printer, isBuilder); + printer->Print("\n\n"); // separate each definition from next code block with empty line + } + + // generate code for nested enums declarations + for (EnumGenerator *gen: enumsDeclaraions) { + gen->generateCode(printer); + printer->Print("\n\n"); // separate each definitions from next code block with empty line + } + + // generate code for fields + for (FieldGenerator *gen: properties) { + gen->generateCode(printer); + printer->Print("\n"); + } + + // generate constructor for builders + if (isBuilder) { + printer->Print("\n"); + generateConstructor(printer); + } + + // generate builder for fair classes + if (!isBuilder) { + printer->Print("\n"); + generateBuilder(printer); + } + + printer->Outdent(); + printer->Print("}"); +} + +ClassGenerator::ClassGenerator(Descriptor const *descriptor) { + simpleName = descriptor->name(); // TODO: think about more careful class naming + modifier = ClassModifier(ClassModifier::CLASS); + + int field_count = descriptor->field_count(); + for (int i = 0; i < field_count; ++i) { + FieldDescriptor const * fieldDescriptor = descriptor->field(i); + properties.push_back(new FieldGenerator(fieldDescriptor)); + } + + int nested_types_count = descriptor->nested_type_count(); + for (int i = 0; i < nested_types_count; ++i) { + Descriptor const * nestedClassDescriptor = descriptor->nested_type(i); + classesDeclarations.push_back(new ClassGenerator(nestedClassDescriptor)); + } + + int enums_declarations_count = descriptor->enum_type_count(); + for (int i = 0; i < enums_declarations_count; ++i) { + EnumDescriptor const * nestedEnumDescriptor = descriptor->enum_type(i); + enumsDeclaraions.push_back(new EnumGenerator(nestedEnumDescriptor)); + } + + +} + +ClassGenerator::~ClassGenerator() { + for (int i = 0; i < properties.size(); ++i) { + delete properties[i]; + } + + for (int i = 0; i < classesDeclarations.size(); ++i) { + delete classesDeclarations[i]; + } + + for (int i = 0; i < enumsDeclaraions.size(); ++i) { + delete enumsDeclaraions[i]; + } +} + +void ClassGenerator::generateBuilder(io::Printer *) const { + +} + +void ClassGenerator::generateConstructor(io::Printer *printer, bool isBuilder) const { + // generate header + printer->Print("private constructor(\n"); + + // place each argument of constructor in separate line for a prettier code + // we indent twice to make arguments indentation larger than indentation of inner block + printer->Indent(); + printer->Indent(); + for (int i = 0; i < properties.size(); ++i) { + // generate argument definition + map vars; + vars["name"] = properties[i] ->simpleName; + vars["field"] = properties[i]->fieldName; + printer->Print(vars, + "$name$: $field$"); + + // if it's last property, then print closing bracket for argument list, otherwise put comma + if (i + 1 == properties.size()) { + printer->Print(") : this()\n"); + printer->Outdent(); + printer->Outdent(); + printer->Print("{"); + printer->Indent(); + } + else { + printer->Print(","); + } + + printer->Print("\n"); + } + + // print body of constructor - just assign arguments to corresponding fields + for (int i = 0; i < properties.size(); ++i) { + map vars; + vars["name"] = properties[i]->simpleName; + printer->Print(vars, + "this.$name$ = $name$" + "\n" + ); + } + printer->Outdent(); + printer->Print("}"); +} + + +const string ClassModifier::getName() const { + string result = ""; + switch (type) { + case CLASS: + result = "class"; + break; + case INTERFACE: + result = "interface"; + break; + } + return result; +} + +ClassModifier::ClassModifier(ClassModifier::Type type) + : type(type) +{ } + +ClassModifier::ClassModifier() { + type = CLASS; +} + + +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google + + diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h new file mode 100644 index 00000000000..732f546f792 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h @@ -0,0 +1,59 @@ +// +// Created by user on 7/11/16. +// + +#ifndef SRC_KOTLIN_CLASS_GENERATOR_H +#define SRC_KOTLIN_CLASS_GENERATOR_H + +#include +#include +#include +#include "kotlin_field_generator.h" +#include "kotlin_enum_generator.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +// wrapper for enum CLASS/INTERFACE with convenience method of getting name +class ClassModifier { +public: + enum Type { + CLASS, + INTERFACE + }; + ClassModifier(); + ClassModifier(Type type); + Type type; + + string const getName() const; +}; + +class ClassGenerator { +public: + ClassModifier modifier; + string simpleName; + vector properties; + vector classesDeclarations; + vector enumsDeclaraions; + + ClassGenerator (Descriptor const * descriptor); + ~ClassGenerator (); + + /** + * Flag isBuilder used for reducing code repeating, as code for class itself + * and for its inner builder are structurally very alike and can be generated + * with very little differences (like changing 'val's to 'var's and etc.) + */ + void generateCode (io::Printer * printer, bool isBuilder = false) const; +private: + void generateBuilder (io::Printer * printer) const; + void generateConstructor(io::Printer * printer, bool isBuilder = false) const; +}; + +} // namespace kotlin +} // namspace compiler +} // namespace protobuf +} // namespace google +#endif //SRC_KOTLIN_CLASS_GENERATOR_H diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc new file mode 100644 index 00000000000..3af18d2a6d5 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.cc @@ -0,0 +1,73 @@ +// +// Created by user on 7/12/16. +// + +#include "kotlin_enum_generator.h" +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + + +EnumValueGenerator::EnumValueGenerator(EnumValueDescriptor const *descriptor) { + simpleName = descriptor->name(); + ordinal = descriptor->number(); +} + +void EnumValueGenerator::generateCode(io::Printer * printer) const { + map vars; + vars["name"] = simpleName; + vars["ordinal"] = std::to_string(ordinal); + printer->Print(vars, + "$name$ ($ordinal$)" + ); +} + +void EnumGenerator::generateCode(io::Printer * printer) const { + /** + * Generate enum class header. + * Note that according to protobuf encoding, wire stores enum fields as ints, + * and client is responsible for proper casting those ints to actual enum values. + * Therefore, we have to add int-constructor in generated Kotlin-enum, and assign + * ordinals for each enum-value properly. + */ + map vars; + vars["name"] = simpleName; + printer->Print(vars, + "enum class $name$(val ord: Int) {" + "\n" + ); + printer->Indent(); + + // Generate enum values. + for (int i = 0; i < enumValues.size(); ++i) { + enumValues[i]->generateCode(printer); + if (i + 1 != enumValues.size()) { + printer->Print(","); + } + printer->Print("\n"); + } + printer->Outdent(); + printer->Print("}"); +} + +EnumGenerator::~EnumGenerator() { + for (int i = 0; i < enumValues.size(); ++i) { + delete enumValues[i]; + } +} + +EnumGenerator::EnumGenerator(EnumDescriptor const *descriptor) { + simpleName = descriptor->name(); + int values_count = descriptor->value_count(); + for (int i = 0; i < values_count; ++i) { + enumValues.push_back(new EnumValueGenerator(descriptor->value(i))); + } +} + +} // namespace kotlin +} // namspace compiler +} // namespace protobuf +} // namespace google diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h new file mode 100644 index 00000000000..aa6d058a579 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_enum_generator.h @@ -0,0 +1,42 @@ +// +// Created by user on 7/12/16. +// + +#ifndef PROTOBUF_KOTLIN_ENUM_GENERATOR_H +#define PROTOBUF_KOTLIN_ENUM_GENERATOR_H + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +class EnumValueGenerator { +public: + EnumValueGenerator(EnumValueDescriptor const * descriptor); + string simpleName; + int ordinal; + + void generateCode(io::Printer *) const; +}; + +class EnumGenerator { +public: + EnumGenerator(EnumDescriptor const * descriptor); + ~EnumGenerator(); + string simpleName; + vector enumValues; + + void generateCode(io::Printer *) const; +}; + +} // namespace kotlin +} // namspace compiler +} // namespace protobuf +} // namespace google + + +#endif //PROTOBUF_KOTLIN_ENUM_GENERATOR_H + diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc new file mode 100644 index 00000000000..6d57c4294a8 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc @@ -0,0 +1,116 @@ +// +// Created by user on 7/12/16. +// + +#include "kotlin_field_generator.h" +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +/** + * Gets equivalent Kotlin-type for a given field descriptor. + * This method takes protobuf field type into account, adding 'List<>' + * for repeated fields, and '?' for optional. + */ +string FieldGenerator::protobufToKotlinField() const { + string preamble = "", + postamble = ""; + switch (modifier) { + case FieldDescriptor::LABEL_REQUIRED: + break; + case FieldDescriptor::LABEL_OPTIONAL: + postamble = "?"; + break; + case FieldDescriptor::LABEL_REPEATED: + preamble = "List <"; + postamble = "> "; + break; + } + return preamble + protobufToKotlinType() + postamble; +} +/** + * Simply maps protobuf field type to corresponding Kotlin. + */ +string FieldGenerator::protobufToKotlinType() const { + FieldDescriptor::Type type = descriptor->type(); + switch(type) { + case FieldDescriptor::TYPE_BOOL: + return "Boolean"; + case FieldDescriptor::TYPE_BYTES: + return ""; // TODO: support bytes type + case FieldDescriptor::TYPE_DOUBLE: + return "Double"; + case FieldDescriptor::TYPE_ENUM: + return string(descriptor->enum_type()->name()); + case FieldDescriptor::TYPE_FIXED32: + // we map uint32 into Int, storing top bit in sign bit + return "Int"; + case FieldDescriptor::TYPE_FIXED64: + // we map uint64 into Long, storing top bit in sign bit + return "Long"; + case FieldDescriptor::TYPE_FLOAT: + return "Float"; + case FieldDescriptor::TYPE_GROUP: + return ""; // @deprecated //TODO: make proper error handling there + case FieldDescriptor::TYPE_INT32: + return "Int"; + case FieldDescriptor::TYPE_INT64: + return "Long"; + case FieldDescriptor::TYPE_MESSAGE: + return string(descriptor->message_type()->name()); + case FieldDescriptor::TYPE_SFIXED32: + return "Int"; + case FieldDescriptor::TYPE_SFIXED64: + return "Long"; + case FieldDescriptor::TYPE_SINT32: + return "Int"; + case FieldDescriptor::TYPE_SINT64: + return "Long"; + case FieldDescriptor::TYPE_STRING: + return "kotlin.String"; + case FieldDescriptor::TYPE_UINT32: + return "Int"; // see notes for TYPE_FIXED32 + case FieldDescriptor::TYPE_UINT64: + return "Long"; // see notes for TYPE_FIXED64 + } +} + +string FieldGenerator::getInitValue() const { + if (descriptor->is_optional()) { + return "null"; + } + if (descriptor->is_repeated()) + return "listOf()"; + return fieldName + "()"; +} + +void FieldGenerator::generateCode(io::Printer *printer) const { + /** Generate Kotlin-code for field. + * Note that we use 'val' everywhere, as we want messages to be immutable. + * For constructing Messages corresponding Builders should be used. + */ + map vars; + vars["name"] = simpleName; + vars["field"] = protobufToKotlinField(); + vars["initValue"] = initValue; + printer->Print(vars, "val $name$ : $field$ = $initValue$"); +} + +FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor) + : descriptor(descriptor) + , modifier(descriptor->label()) + , simpleName(descriptor->name()) + , fieldName(protobufToKotlinField()) + , initValue(getInitValue()) +{ } + +} // namespace kotlin +} // namspace compiler +} // namespace protobuf +} // namespace google \ No newline at end of file diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h new file mode 100644 index 00000000000..ebdf8f12f37 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h @@ -0,0 +1,40 @@ +// +// Created by user on 7/12/16. +// + +#ifndef PROTOBUF_KOTLIN_FIELD_GENERATOR_H +#define PROTOBUF_KOTLIN_FIELD_GENERATOR_H + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +class FieldGenerator { +private: + FieldDescriptor const * descriptor; + string protobufToKotlinField() const; + string protobufToKotlinType () const; + string getInitValue() const; + +public: + FieldDescriptor::Label modifier; + string simpleName; + string fieldName; + string initValue; + + void generateCode(io::Printer *) const; + FieldGenerator(FieldDescriptor const * descriptor); + +}; + +} // namespace kotlin +} // namspace compiler +} // namespace protobuf +} // namespace google + +#endif //PROTOBUF_KOTLIN_FIELD_GENERATOR_H diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc new file mode 100644 index 00000000000..5ff54c22c5d --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_file_generator.cc @@ -0,0 +1,65 @@ +// +// Created by user on 7/11/16. +// + +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include "kotlin_file_generator.h" +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +void FileGenerator::generateCode(io::Printer *printer, std::vector & classes) const { + for (int i = 0; i < classes.size(); ++i) { + classes[i]->generateCode(printer); + printer->Print("\n\n"); + } +} + +bool FileGenerator::Generate(const FileDescriptor *file, const string ¶meter, GeneratorContext *context, + string *error) const { + std::vector classes; + + //TODO: maybe wrap work with class names and stuff in separate class (like in Java implementation) + // Get output file name + string const proto_file_name = file->name(); + size_t file_extension_index = proto_file_name.find(".proto"); + string const file_name = proto_file_name.substr(0, file_extension_index) + ".kt"; + + + google::protobuf::scoped_ptr output(context->Open(file_name)); + io::Printer printer(output.get(), '$', /* annotation_collector = */ NULL); + + // Create Generators for all top-level messages + int topLevelMessagesCount = file->message_type_count(); + for (int i = 0; i < topLevelMessagesCount; ++i) { + Descriptor const * descriptor = file->message_type(i); + ClassGenerator * cgen = new ClassGenerator(descriptor); + classes.push_back(cgen); + } + + // Generate code and clean up + generateCode(&printer, classes); + for (int i = 0; i < classes.size(); ++i) { + delete classes[i]; + } + return true; +} + +FileGenerator::FileGenerator() { } + +FileGenerator::~FileGenerator() { +} + + +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_file_generator.h b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_file_generator.h new file mode 100644 index 00000000000..37d3ab21110 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/kotlin_file_generator.h @@ -0,0 +1,37 @@ +// +// Created by user on 7/11/16. +// + +#ifndef SRC_KOTLIN_FILE_GENERATOR_H +#define SRC_KOTLIN_FILE_GENERATOR_H + +#include "kotlin_class_generator.h" +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + + +class FileGenerator : public CodeGenerator { +public: + FileGenerator(); + ~FileGenerator(); + + void generateCode(io::Printer *, std::vector &) const; + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + GeneratorContext* context, + string* error) const; +}; + +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif //SRC_KOTLIN_FILE_GENERATOR_H diff --git a/proto/compiler/google/protobuf/compiler/kotlin/src/main.cc b/proto/compiler/google/protobuf/compiler/kotlin/src/main.cc new file mode 100644 index 00000000000..04e3267731a --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/src/main.cc @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +// TODO(teboring): Add it back when php implementation is ready +// #include +#include +#include +#include +#include +#include "kotlin_file_generator.h" +#include + +int main(int argc, const char* const * argv) { + google::protobuf::compiler::CommandLineInterface cli; + + // Support generation of C++ source and headers. + google::protobuf::compiler::cpp::CppGenerator cpp_generator; + cli.RegisterGenerator("--cpp_out", &cpp_generator, + "Generate C++ source and header."); + + google::protobuf::compiler::kotlin::FileGenerator kotlinGenerator; + cli.RegisterGenerator("--kotlin_out", &kotlinGenerator, + "Generate Foo file."); + + return cli.Run(argc, argv); +} \ No newline at end of file diff --git a/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt b/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt new file mode 100644 index 00000000000..e12d845444d --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.kt @@ -0,0 +1,25 @@ +class Person private constructor () { + class PhoneNumber private constructor () { + val number : kotlin.String? = null + val type : PhoneType? = null + + } + + enum class PhoneType(val ord: Int) { + MOBILE (0), + HOME (1), + WORK (2) + } + + val name : kotlin.String? = null + val id : Int? = null + val email : kotlin.String? = null + val phones : List = listOf() + +} + +class AddressBook private constructor () { + val people : List = listOf() + +} + diff --git a/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.proto b/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.proto new file mode 100644 index 00000000000..23cc2f97e76 --- /dev/null +++ b/proto/compiler/google/protobuf/compiler/kotlin/test/addressbook.proto @@ -0,0 +1,47 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +package tutorial; +// [END declaration] + +// [START java_declaration] +option java_package = "com.example.tutorial"; +option java_outer_classname = "AddressBookProtos"; +// [END java_declaration] + +// [START csharp_declaration] +option csharp_namespace = "Google.Protobuf.Examples.AddressBook"; +// [END csharp_declaration] + +// [START messages] +message Person { + string name = 1; + int32 id = 2; // Unique ID number for this person. + string email = 3; + + enum PhoneType { + MOBILE = 0; + HOME = 1; + WORK = 2; + } + + message PhoneNumber { + string number = 1; + PhoneType type = 2; + } + + repeated PhoneNumber phones = 4; +} + +// Our address book file is just one of these. +message AddressBook { + repeated Person people = 1; +} +// [END messages]