)]}'
{
  "commit": "f5362e11fd25f88ea0fb044485b8a815996b17db",
  "tree": "c6cea1045b2df5cd1da9db2d0fc4159c2efe170b",
  "parents": [
    "de57caa1fd813e131f1ef52e04fe9722249ed988"
  ],
  "author": {
    "name": "Ben Gordon",
    "email": "41341775+beng-toast@users.noreply.github.com",
    "time": "Wed Jul 10 09:58:38 2019 -0400"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Wed Jul 10 09:58:38 2019 -0400"
  },
  "message": "Requesting extension id 1072\n\nThis library supports an idiomatic proto3 protobuf generator for kotlin.\r\nThe library will be open sourced by Toast Inc under the Apache2 license, and is currently used in production at Toast.\r\nThe following is the readme.md that will be released with the code by the end of Q4 2019.\r\n\r\nSupports only the Protocol Buffers language version 3.\r\n\r\n#### Features\r\n- Clean data class generation\r\n- Oneof types handled as sealed classes\r\n- JavaDoc comments on generated code\r\n- Deprecation option pass-through to Kotlin\u0027s `@Deprecated` annotation\r\n- Protokt-specific options: non-null types, wrapper types, interface implementation,\r\nand more\r\n- Tight integration with Protobuf\u0027s Java library: compatibility with its well-known\r\ntypes and usage of CodedInputStream and CodedOutputStream for best performance\r\n\r\n#### Not yet implemented\r\n- Kotlin native support\r\n- Kotlin JS support\r\n- Support for gRPC service generation\r\n- Protobuf JSON support\r\n\r\nSee examples in [protokt-testing](https://github.com/toasttab/protokt/tree/master/protokt-testing).\r\n\r\n### Generated Code\r\nGenerated code is placed in `\u003cbuildDir\u003e/generated-sources/main/protokt`.\r\n\r\nA simple example:\r\n```proto\r\nsyntax \u003d \"proto3\";\r\n\r\npackage com.protokt.sample;\r\n\r\nmessage Sample {\r\n  string sample_field \u003d 1;\r\n}\r\n```\r\n\r\nwill produce:\r\n```kotlin\r\n/*\r\n * Generated by protokt. Do not modify.\r\n */\r\npackage com.protokt.sample\r\n\r\nimport com.toasttab.protokt.rt.*\r\n\r\ndata class Sample(\r\n    val sampleField: String,\r\n    val unknown: Map\u003cInt, Unknown\u003e \u003d emptyMap()\r\n) : KtMessage {\r\n    @Suppress(\"UNUSED\")\r\n    constructor(\r\n        sampleField: String \u003d \"\"\r\n    ) : this(\r\n        sampleField,\r\n        emptyMap()\r\n    )\r\n\r\n    override val messageSize by lazy { sizeof() }\r\n\r\n    override fun serialize(serializer: KtMessageSerializer) {\r\n        if (sampleField.isNotEmpty()) {\r\n            serializer.write(Tag(10)).write(sampleField)\r\n        }\r\n        if (unknown.isNotEmpty()) {\r\n            serializer.writeUnknown(unknown)\r\n        }\r\n    }\r\n\r\n    private fun sizeof(): Int {\r\n        var res \u003d 0\r\n        if (sampleField.isNotEmpty()) {\r\n            res +\u003d sizeof(Tag(1)) + sizeof(sampleField)\r\n        }\r\n        res +\u003d unknown.entries.sumBy { it.value.sizeof() }\r\n        return res\r\n    }\r\n\r\n    companion object Deserializer : KtDeserializer\u003cSample\u003e {\r\n        override fun deserialize(deserializer: KtMessageDeserializer): Sample {\r\n            var sampleField \u003d \"\"\r\n            val unknown \u003d mutableMapOf\u003cInt, Unknown\u003e()\r\n            while (true) {\r\n                when (deserializer.readTag()) {\r\n                    0 -\u003e\r\n                        return Sample(\r\n                            sampleField,\r\n                            unknown\r\n                        )\r\n                    10 -\u003e sampleField \u003d deserializer.readString()\r\n                    else -\u003e {\r\n                        val unk \u003d deserializer.readUnknown()\r\n                        unknown[unk.fieldNum] \u003d unknown[unk.fieldNum].let {\r\n                            when (it) {\r\n                                null -\u003e unk\r\n                                else -\u003e\r\n                                    when (val v \u003d it.value) {\r\n                                        is ListVal -\u003e\r\n                                            Unknown(unk.fieldNum, ListVal(v.value + unk.value))\r\n                                        else -\u003e\r\n                                            Unknown(unk.fieldNum, ListVal(listOf(v, unk.value)))\r\n                                    }\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n#### Runtime Notes\r\n##### Package\r\nThe Kotlin package of a generated file can be overridden from protobuf package with the `(protokt).package` option:\r\n```proto\r\nsyntax \u003d \"proto3\";\r\n\r\nimport \"protokt.proto\";\r\n\r\npackage com.example;\r\n\r\noption (protokt).package \u003d \"com.package\";\r\n```\r\n\r\n##### Message\r\nEach protokt message implements the `KtMessage` interface. `KtMessage` defines the `serialize()`\r\nmethod and its overloads which can serialize to a byte array, a `KtMessageSerializer`, or on the JVM,\r\nan `OutputStream`.\r\n\r\nEach protokt message has a companion object `Deserializer` that implements the `KtDeserializer`\r\ninterface, which provides the `deserialize()` method and its overloads to construct an\r\ninstance of the message from a byte array, a Java InputStream, or others.\r\n\r\nIn order to enjoy the full benefits of Kotlin data classes, byte arrays are wrapped in the\r\nprotokt `Bytes` class, which provides appropriate `equals()` and `hashCode()` implementations.\r\n\r\n##### Enums\r\nEnum fields are generated as data classes with a single integer field. Kotlin enum classes are\r\nclosed and cannot retain unknown values, and protobuf requires that unknown enum values are\r\npreserved for reserialization. This compromise exposes a constructor taking an integer, but the\r\n`from(value: Int)` on an enum\u0027s `Deserializer` should be preferred as it avoids instantiation\r\nwhen possible.\r\n\r\n\r\nOther notes:\r\n- `optimize_for` is ignored.\r\n- `repeated` fields are deserialized to Lists.\r\n- `map` fields are deserialized to Maps.\r\n- `oneof` fields are represented as data class subtypes of a sealed base class with a single property.\r\n\r\n### Extensions\r\nSee examples of each option in the [protokt-options](https://github.com/toasttab/protokt/tree/master/protokt-testing/protokt-options/src/main/proto)\r\nmodule. All protokt-specific options require importing `protokt.proto` in the protocol file.\r\n#### Wrapper Types\r\nSometimes a field on a protobuf message corresponds to a concrete nonprimitive type. In\r\nstandard protobuf the user would be responsible for this extra transformation, but the\r\nprotokt wrapper type option allows specification of a converter that will automatically\r\nencode and decode custom types to protobuf primitives and well-known types. Some standard\r\ntypes are implemented in\r\n[protokt-extensions](https://github.com/toasttab/protokt/tree/master/protokt-extensions/src/main/kotlin/com/toasttab/protokt/ext).\r\n\r\nWrap a field by invoking the `(protokt_property).wrap` option:\r\n```proto\r\nmessage DateWrapperMessage {\r\n  int64 date \u003d 1 [\r\n    (protokt_property).wrap \u003d \"java.util.Date\"\r\n  ];\r\n}\r\n```\r\n\r\nConverters implement the `Converter` interface:\r\n```kotlin\r\ninterface Converter\u003cS: Any, T: Any\u003e {\r\n    val wrapper: KClass\u003cS\u003e\r\n\r\n    fun wrap(unwrapped: T): S\r\n\r\n    fun unwrap(wrapped: S): T\r\n}\r\n```\r\n\r\nand protokt will reference the converter\u0027s methods to wrap and unwrap from protobuf primitives:\r\n```kotlin\r\nobject DateConverter : Converter\u003cDate, Long\u003e {\r\n    override val wrapper \u003d Date::class\r\n\r\n    override fun wrap(unwrapped: Long) \u003d\r\n        Date(unwrapped)\r\n\r\n    override fun unwrap(wrapped: Date) \u003d\r\n        wrapped.time\r\n}\r\n```\r\n\r\n```kotlin\r\ndata class WrapperModel(\r\n    val date: java.util.Date,\r\n    ...\r\n) : KtMessage {\r\n    ...\r\n    override fun serialize(serializer: KtMessageSerializer) {\r\n        serializer.write(Tag(10)).write(Int64(DateConverter.unwrap(date)))\r\n        ...\r\n    }\r\n\r\n    override fun deserialize(deserializer: KtMessageDeserializer): WrapperModel {\r\n        var date \u003d 0L\r\n\r\n        while (true) {\r\n            when (deserializer.readTag()) {\r\n                0 -\u003e\r\n                    return WrapperModel(\r\n                        DateConverter.wrap(date),\r\n                        ...\r\n                    )\r\n                ...\r\n            }\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nConverters can also implement the `OptimizedSizeofConverter` interface adding `sizeof()`,\r\nwhich allows them to optimize the calculation of the wrapper\u0027s size rather than unwrap\r\nthe object twice. For example, a UUID is always 16 bytes:\r\n\r\n```kotlin\r\nobject UuidConverter : OptimizedSizeofConverter\u003cUUID, ByteArray\u003e {\r\n    override val wrapper \u003d UUID::class\r\n\r\n    private val sizeofProxy \u003d ByteArray(16)\r\n\r\n    override fun sizeof(wrapped: UUID) \u003d\r\n        sizeof(sizeofProxy)\r\n\r\n    override fun wrap(unwrapped: ByteArray): UUID {\r\n        require(unwrapped.size \u003d\u003d 16) {\r\n            \"input must have size 16; had ${unwrapped.size}\"\r\n        }\r\n\r\n        return ByteBuffer.wrap(unwrapped)\r\n            .run { UUID(long, long) }\r\n    }\r\n\r\n    override fun unwrap(wrapped: UUID) \u003d\r\n        ByteBuffer.allocate(16)\r\n            .putLong(wrapped.mostSignificantBits)\r\n            .putLong(wrapped.leastSignificantBits)\r\n            .array()\r\n}\r\n```\r\n\r\nRather than convert a UUID to a byte array both for size calculation and for serialization\r\n(which is what a naïve implementation would do), UuidConverter always returns the size of a\r\nconstant 16-byte array.\r\n\r\nIf the wrapper type is in the same package as the generated protobuf message, then it\r\ndoes not need a fully-qualified name. Custom wrapper type converters can be in the same module as\r\nprotobuf types that reference them. In order to use any wrapper type defined in\r\n`protokt-extensions`, the module must be included as a dependency:\r\n\r\n```groovy\r\ndependencies {\r\n    implementation \u0027com.toasttab.protokt:protokt-extensions:0.0.3\u0027\r\n}\r\n```\r\n\r\n#### Interface implementation\r\nTo avoid the need to create domain-specific objects from protobuf messages you can declare\r\nthat a protobuf message implements a custom interface with properties and default methods.\r\n\r\n```kotlin\r\npackage com.protokt.sample\r\n\r\ninterface Model {\r\n    val id: String\r\n}\r\n```\r\n\r\n```proto\r\npackage com.protokt.sample;\r\n\r\nmessage ImplementsSampleMessage {\r\n  option (protokt_class).implements \u003d \"Model\";\r\n\r\n  string id \u003d 1;\r\n}\r\n```\r\n\r\nIf the wrapper interface is in the same package as the generated protobuf message, then it\r\ndoes not need a fully-qualified name. Wrapper interfaces cannot be used by protobuf messages\r\nin the same module that defines them; the dependency must be declared with`protoktExtensions`\r\nin `build.gradle`:\r\n\r\n```groovy\r\ndependencies {\r\n    protoktExtensions project(\u0027:api-module\u0027)\r\n}\r\n```\r\n\r\n#### Nonnull fields\r\nIf there is a message that has no meaning whatsoever when a particular field is missing, you\r\ncan emulate proto2\u0027s `required` key word by using the `(protokt_oneof).non_null` option:\r\n\r\n```proto\r\nmessage Sample {\r\n}\r\n\r\nmessage NonNullSampleMessage {\r\n  Sample non_null_sample \u003d 1 [\r\n    (protokt_property).non_null \u003d true\r\n  ];\r\n}\r\n```\r\n\r\nGenerated code will not have a nullable type so the field can be referenced without using\r\nKotlin\u0027s `!!`.\r\n\r\nOneof fields can also be declared non-null:\r\n\r\n```proto\r\nmessage NonNullSampleMessage {\r\n  oneof non_null_oneof {\r\n    option (protokt_oneof).non_null \u003d true;\r\n\r\n    string message \u003d 2;\r\n  }\r\n}\r\n```\r\n\r\nNote that deserialization of a message with a non-nullable field will fail if the\r\nmessage being decoded does not contain an instance of the required field.\r\n\r\n#### BytesSlice\r\nWhen reading messages that contain other serialized messages as `bytes` fields, protokt can\r\nkeep a reference to the originating byte array to prevent a large copy operation on\r\ndeserialization. This can be desirable when the wrapping message is a thin metadata shim and\r\ndoesn\u0027t include much memory overhead:\r\n\r\n```proto\r\nmessage SliceModel {\r\n  int64 version \u003d 1;\r\n\r\n  bytes encoded_message \u003d 2 [\r\n    (protokt_property).bytes_slice \u003d true\r\n  ];\r\n}\r\n```\r\n\r\n### Usage\r\n\r\n#### Gradle\r\n\r\n```groovy\r\nbuildscript {\r\n    dependencies {\r\n        classpath \"com.toasttab.protokt:protokt-gradle-plugin:0.0.3\"\r\n    }\r\n}\r\n\r\napply plugin: \u0027com.toasttab.protokt\u0027\r\n```\r\n\r\nThis will automatically download and install protokt, apply the Google protobuf plugin,\r\nand configure all the necessary boilerplate. By default it will also add `protokt-runtime`\r\nto the api scope of the project, and `protobuf-java` to the implementation scope.\r\n\r\nIf your project is pure Kotlin you may run into the following error:\r\n\r\n```\r\nExecution failed for task \u0027:compileJava\u0027.\r\n\u003e error: no source files\r\n```\r\n\r\nTo work around it, disable all `JavaCompile` tasks in the project:\r\n\r\n```groovy\r\ntasks.withType(JavaCompile) {\r\n    enabled \u003d false\r\n}\r\n```\r\n\r\nor:\r\n```groovy\r\ncompileJava.enabled \u003d false\r\n```\r\n\r\n#### Command line code generation\r\n\r\n```bash\r\nprotokt-codegen$ ./gradlew assemble [OR ./gradlew installDist]\r\n\r\nprotokt-codegen$ ./run-protokt.sh -h\r\n\r\nprotokt-codegen$ ./run-protokt.sh \\\r\n  -out\u003d../kotlin \\\r\n  -pkg\u003dcom.toasttab.protokt.conformance \\\r\n  -file\u003dconformance.proto \\\r\n  -cp\u003d../build/libs/protokt-codegen-0.0.3-SNAPSHOT-all.jar \\\r\n  -plugin\u003d../bin/protokt.sh\r\n```\r\n\r\n### Contribution\r\nTo enable rapid development of the code generator, the protobuf conformance tests have been \r\ncompiled and included in the protokt-testing project. They run on Mac OS 10.14+ and Ubuntu\r\n16.04 x86-64.\r\n\r\nPublish the plugin to the integration repository:\r\n```bash\r\nprotokt$ ./gradlew publishToIntegrationRepository\r\n```\r\n\r\nThen run the tests from `protokt-testing`:\r\n```bash\r\nprotokt-testing$ ./gradlew protokt-conformance-tests:test\r\n```\r\n\r\nAll integration tests can be run with:\r\n```\r\nprotokt-testing$ ./gradlew test\r\n```",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "f240f0c8bc5da753e5ea189a67e166d83bd69fa8",
      "old_mode": 33188,
      "old_path": "docs/options.md",
      "new_id": "dbc0998e3b9a1aa4f0194bfc26deb8cf08c33821",
      "new_mode": 33188,
      "new_path": "docs/options.md"
    }
  ]
}
