Skip to content

Commit

Permalink
Merge branch 'main' into remove-support-for-kotlin-js-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewparmet committed Dec 15, 2024
2 parents 13de1ba + 3a9fe8f commit 8dbd600
Show file tree
Hide file tree
Showing 60 changed files with 1,877 additions and 1,182 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*.log

.kotlin
.gradle
build
out
Expand Down
68 changes: 21 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# protokt
# protokt

[![Github Actions](https://github.com/open-toast/protokt/actions/workflows/ci.yml/badge.svg)](https://github.com/open-toast/protokt/actions/workflows/ci.yml)
[![Maven Central](https://img.shields.io/maven-central/v/com.toasttab.protokt/protokt-runtime)](https://search.maven.org/artifact/com.toasttab.protokt/protokt-runtime)
Expand All @@ -23,21 +23,21 @@ is represented as `String?`, etc.
CodedOutputStream for best performance
- (JS) Built on protobufJS for best performance
- gRPC [method descriptor and service descriptor generation](#grpc-code-generation)
for use with [grpc-java](#integrating-with-grpcs-java-api),
for use with [grpc-java](#integrating-with-grpcs-java-api),
[grpc-kotlin](#integrating-with-grpcs-kotlin-api), and
[grpc-node](#integrating-with-grpcs-nodejs-api) (experimental) (see examples in [examples](examples))

### Not yet implemented

- Kotlin/Native support
- Protobuf JSON support
- Protobuf JSON support

### Compatibility

The Gradle plugin requires Java 8+ and Gradle 5.6+. It runs on recent versions of
MacOS, Linux, and Windows.

The runtime and generated code are compatible with Kotlin 1.8+, Java 8+, and Android 4.4+.
The runtime and generated code are compatible with Kotlin 1.8+, Java 8+, and Android 4.4+.

## Usage

Expand Down Expand Up @@ -354,7 +354,7 @@ while using the lite runtime:
protokt {
generate {
descriptors = false
}
}
}
```

Expand Down Expand Up @@ -639,8 +639,8 @@ Wrapper types that wrap protobuf messages are nullable. For example,
can be made non-nullable by using the non-null option described below.

Wrapper types that wrap protobuf primitives, for example `java.util.UUID`
which wraps `bytes`, are nullable when they cannot wrap their wrapped type's
default value. Converters must override `acceptsDefaultValue` to be `false` in
which wraps `bytes`, are nullable when they cannot wrap their wrapped type's
default value. Converters must override `acceptsDefaultValue` to be `false` in
these cases. For example, a UUID cannot wrap an empty byte array and each of
the following declarations will produce a nullable property:
Expand Down Expand Up @@ -689,38 +689,21 @@ for each message defined in
### Non-null fields
If a message has no meaning whatsoever when a particular non-scalar field is
missing, you can emulate proto2's `required` key word by using the
`(protokt.v1.property).non_null` option:
`(protokt.v1.property).generate_non_null_accessor` option:

```protobuf
message Sample {}

message NonNullSampleMessage {
Sample non_null_sample = 1 [
(protokt.v1.property).non_null = true
(protokt.v1.property).generate_non_null_accessor = true
];
}
```

Generated code will not have a nullable type, so the field can be referenced
Generated code will include a non-null accessor prefixed with `require`, so the field can be referenced
without using Kotlin's `!!`.
Oneof fields can also be declared non-null:
```protobuf
message NonNullSampleMessage {
oneof non_null_oneof {
option (protokt.v1.oneof).non_null = true;
string message = 1;
}
}
```
Note that deserialization of a message with a non-nullable field will fail if the
message being decoded does not contain an instance of the required field.
This functionality will likely be removed.
### Interface implementation
#### Messages
Expand Down Expand Up @@ -768,21 +751,23 @@ message ImplementsWithDelegate {
option (protokt.v1.class).implements = "Model2 by modelTwo";
ImplementsModel2 model_two = 1 [
(protokt.v1.property).non_null = true
(protokt.v1.property).generate_non_null_accessor = true
];
}
```
Note that the `by` clause references the field by its lower camel case name.
Properties on delegate interfaces must be nullable since fields themselves
may not be present on the wire.
#### Oneof Fields
Oneof fields can declare that they implement an interface with the
Oneof fields can declare that they implement an interface with the
`(protokt.v1.oneof).implements` option. Each possible field type of the oneof must
also implement the interface. This allows access of common properties without a
`when` statement that always ultimately extracts the same property.
Suppose you have a domain object MyObjectWithConfig that has a non-null configuration
Suppose you have a domain object MyObjectWithConfig that has a configuration
that specifies a third-party server for communication. For flexibility, this
configuration will be modifiable by the server and versioned by a simple integer.
To hasten subsequent loading of the configuration, a client may save a resolved
Expand Down Expand Up @@ -816,7 +801,6 @@ message MyObjectWithConfig {
];
oneof Config {
option (protokt.v1.oneof).non_null = true;
option (protokt.v1.oneof).implements = "com.toasttab.example.Config";
ServerSpecified server_specified = 2;
Expand All @@ -836,9 +820,7 @@ message ServerSpecified {
message ClientResolved {
option (protokt.v1.class).implements = "com.toasttab.example.Config by config";
ServerSpecified config = 1 [
(protokt.v1.property).non_null = true
];
ServerSpecified config = 1;
bytes last_known_address = 2 [
(protokt.v1.property).wrap = "java.net.InetAddress"
Expand All @@ -853,7 +835,7 @@ Protokt will generate:
public class MyObjectWithConfig private constructor(
@GeneratedProperty(1)
public val id: UUID?,
public val config: Config,
public val config: Config?,
public val unknownFields: UnknownFieldSet = UnknownFieldSet.empty()
) : AbstractMessage() {
Expand Down Expand Up @@ -906,18 +888,10 @@ accessing the property via a `when` expression:
```kotlin
fun printVersion(config: MyObjectWithConfig.Config) {
println(config.version)
println(config?.version)
}
```
This pattern is dangerous: since the oneof must be marked non-nullable, you
cannot compatibly add new implementing fields to a producer before a consumer
is updated with the new generated code. The old consumer will attempt to
deserialize the new field as an unknown field and the non-null assertion on the
oneof field during the constructor call will fail.
This functionality will likely be removed.
### BytesSlice
When reading messages that contain other serialized messages as `bytes` fields,
Expand Down Expand Up @@ -946,7 +920,7 @@ protokt {
generate {
grpcDescriptors = true
grpcKotlinStubs = true
}
}
}
```

Expand Down Expand Up @@ -1061,7 +1035,7 @@ Protokt generates complete server and client stub implementations for use with N
The generated implementations are nearly the same as those generated by grpc-kotlin and
are supported by an analogous runtime library in ServerCalls and ClientCalls objects.
These implementations are alpha-quality and for demonstration only. External contributions
These implementations are alpha-quality and for demonstration only. External contributions
to harden the implementation are welcome. They use the same `grpcDescriptors` and
`grpcKotlinStubs` plugin options to control code generation.
Expand Down Expand Up @@ -1104,7 +1078,7 @@ protokt % protoc \

## Contribution

Community contributions are welcome. See the
Community contributions are welcome. See the
[contribution guidelines](CONTRIBUTING.md) and the project
[code of conduct](CODE-OF-CONDUCT.md).

Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/LocalProtoktBuild.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fun Project.localProtokt(disableJava: Boolean = true) {

afterEvaluate {
tasks.withType<GenerateProtoTask> {
inputs.dir("$rootDir/protokt-codegen/build/install/$CODEGEN_NAME")
dependsOn(":protokt-codegen:installDist")
}
}
Expand Down
6 changes: 5 additions & 1 deletion buildSrc/src/main/kotlin/protokt.jvm-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Toast, Inc.
* Copyright (c) 2024 Toast, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,6 +13,8 @@
* limitations under the License.
*/

import org.jetbrains.kotlin.gradle.dsl.KotlinVersion

plugins {
id("protokt.common-conventions")
kotlin("jvm")
Expand All @@ -22,4 +24,6 @@ javaBasedProjectConventions()

kotlin {
jvmToolchain(libs.versions.java.get().toInt())
compilerOptions.apiVersion = KotlinVersion.KOTLIN_1_8
compilerOptions.languageVersion = KotlinVersion.KOTLIN_1_8
}
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ public final class protokt/v1/FieldOptions : protokt/v1/AbstractMessage {
public fun equals (Ljava/lang/Object;)Z
public final fun getBytesSlice ()Z
public final fun getDeprecationMessage ()Ljava/lang/String;
public final fun getGenerateNonNullAccessor ()Z
public final fun getKeyWrap ()Ljava/lang/String;
public final fun getNonNull ()Z
public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet;
public final fun getValueWrap ()Ljava/lang/String;
public final fun getWrap ()Ljava/lang/String;
Expand All @@ -392,15 +392,15 @@ public final class protokt/v1/FieldOptions$Builder {
public final fun build ()Lprotokt/v1/FieldOptions;
public final fun getBytesSlice ()Z
public final fun getDeprecationMessage ()Ljava/lang/String;
public final fun getGenerateNonNullAccessor ()Z
public final fun getKeyWrap ()Ljava/lang/String;
public final fun getNonNull ()Z
public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet;
public final fun getValueWrap ()Ljava/lang/String;
public final fun getWrap ()Ljava/lang/String;
public final fun setBytesSlice (Z)V
public final fun setDeprecationMessage (Ljava/lang/String;)V
public final fun setGenerateNonNullAccessor (Z)V
public final fun setKeyWrap (Ljava/lang/String;)V
public final fun setNonNull (Z)V
public final fun setUnknownFields (Lprotokt/v1/UnknownFieldSet;)V
public final fun setValueWrap (Ljava/lang/String;)V
public final fun setWrap (Ljava/lang/String;)V
Expand Down Expand Up @@ -593,13 +593,12 @@ public final class protokt/v1/MethodOptions$Deserializer : protokt/v1/AbstractDe

public final class protokt/v1/OneofOptions : protokt/v1/AbstractMessage {
public static final field Deserializer Lprotokt/v1/OneofOptions$Deserializer;
public synthetic fun <init> (ZLjava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lprotokt/v1/UnknownFieldSet;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun copy (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/OneofOptions;
public static fun deserialize (Lprotokt/v1/Reader;)Lprotokt/v1/OneofOptions;
public fun equals (Ljava/lang/Object;)Z
public final fun getDeprecationMessage ()Ljava/lang/String;
public final fun getImplements ()Ljava/lang/String;
public final fun getNonNull ()Z
public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet;
public fun hashCode ()I
public static final fun invoke (Lkotlin/jvm/functions/Function1;)Lprotokt/v1/OneofOptions;
Expand All @@ -613,11 +612,9 @@ public final class protokt/v1/OneofOptions$Builder {
public final fun build ()Lprotokt/v1/OneofOptions;
public final fun getDeprecationMessage ()Ljava/lang/String;
public final fun getImplements ()Ljava/lang/String;
public final fun getNonNull ()Z
public final fun getUnknownFields ()Lprotokt/v1/UnknownFieldSet;
public final fun setDeprecationMessage (Ljava/lang/String;)V
public final fun setImplements (Ljava/lang/String;)V
public final fun setNonNull (Z)V
public final fun setUnknownFields (Lprotokt/v1/UnknownFieldSet;)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,16 @@ extend google.protobuf.MessageOptions {
}

message FieldOptions {
// Makes a message-type field non-nullable in the generated Kotlin code.
// Beware that deserialization will NPE if the field is missing from the
// protobuf payload. Adding a non-null field to an existing message is a
// backwards-incompatible change.
// Generates a non-nullable accessor.
//
// For example:
//
// message Foo {
// string id = 1 [(protokt.v1.property).non_null = true];
// Bar id = 1 [(protokt.v1.property).generate_non_null_accessor = true];
// }
bool non_null = 1;
//
// will generate an accessor called [requireId] that has the non-nullable type [Bar] (as opposed to [Bar?]).
bool generate_non_null_accessor = 1;

// Expose a wrapper class instead of a raw protobuf type.
//
Expand Down Expand Up @@ -125,22 +124,7 @@ extend google.protobuf.FieldOptions {
}

message OneofOptions {
// Makes a oneof field non-nullable in generated Kotlin code. Beware that
// deserialization will NPE if the field is missing from the protobuf payload.
// Adding a non-null field to an existing message is a backwards-incompatible
// change.
//
// For example:
//
// message Message {
// oneof some_field_name {
// option (protokt.v1.oneof).non_null = true;
//
// string id = 1;
// }
// }
//
bool non_null = 1;
reserved 1;

// Make the sealed class implement an interface, enforcing the presence of a
// property in each possible variant. Scoping rules are the same as those
Expand Down
6 changes: 4 additions & 2 deletions gradle-plugin-integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.diffplug.gradle.spotless.SpotlessExtension
import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand Down Expand Up @@ -99,13 +100,14 @@ subprojects {
}

withType<KotlinCompile> {
kotlinOptions {
compilerOptions {
allWarningsAsErrors = true

apiVersion =
apiVersion = KotlinVersion.fromVersion(
System.getProperty("kotlin-integration.version")
?.substringBeforeLast(".")
?: libs.versions.kotlin.get().substringBeforeLast(".")
)

languageVersion = apiVersion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ message TestMessage {
}

message Submessage {
TestMessage qux = 1 [(protokt.v1.property).non_null = true];
TestMessage qux = 1 [(protokt.v1.property).generate_non_null_accessor = true];
}
}
Loading

0 comments on commit 8dbd600

Please sign in to comment.