Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Value (former inline) class serialization support #103

Merged
merged 2 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugins {
java
id("java-library")
kotlin("jvm") version Libs.kotlinVersion
id("org.jetbrains.kotlin.plugin.serialization") version Libs.kotlinVersion
kotlin("plugin.serialization") version Libs.kotlinVersion
id("maven-publish")
signing
// id("org.jetbrains.dokka") version Libs.dokkaVersion
Expand Down Expand Up @@ -46,8 +46,8 @@ dependencies {

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.apiVersion = "1.4"
kotlinOptions.languageVersion = "1.4"
kotlinOptions.apiVersion = "1.5"
kotlinOptions.languageVersion = "1.5"
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

Expand Down Expand Up @@ -156,4 +156,4 @@ publishing {
}
}
}
}
}
6 changes: 3 additions & 3 deletions buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
object Libs {

const val kotlinVersion = "1.4.32"
const val kotlinVersion = "1.5.20-RC"
// const val dokkaVersion = "1.4.20"
const val kotestGradlePlugin = "0.2.6"
const val kotestGradlePlugin = "0.3.8"
const val versionsPlugin = "0.38.0"

object Kotest {
Expand All @@ -14,7 +14,7 @@ object Libs {
}

object Kotlinx {
private const val version = "1.1.0"
private const val version = "1.2.1"
const val serializationCore = "org.jetbrains.kotlinx:kotlinx-serialization-core:$version"
const val serializationJson = "org.jetbrains.kotlinx:kotlinx-serialization-json:$version"
}
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fun schemaFor(serializersModule: SerializersModule,

val schemaFor: SchemaFor = when (underlying) {
is AvroDescriptor -> SchemaFor.const(underlying.schema(annos, serializersModule, namingStrategy))
else -> when (descriptor.kind) {
else -> when (descriptor.carrierDescriptor.kind) {
PrimitiveKind.STRING -> SchemaFor.StringSchemaFor
PrimitiveKind.LONG -> SchemaFor.LongSchemaFor
PrimitiveKind.INT -> SchemaFor.IntSchemaFor
Expand Down Expand Up @@ -209,3 +209,8 @@ fun schemaFor(serializersModule: SerializersModule,

return if (descriptor.isNullable) NullableSchemaFor(schemaFor, annos) else schemaFor
}

// copy-paste from kotlinx serialization because it internal
@ExperimentalSerializationApi
internal val SerialDescriptor.carrierDescriptor: SerialDescriptor
get() = if (isInline) getElementDescriptor(0) else this
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.github.avrokotlin.avro4k.schema

import java.util.Locale

interface NamingStrategy {
fun to(name: String): String = name
}
Expand All @@ -11,14 +9,14 @@ object DefaultNamingStrategy : NamingStrategy {
}

object PascalCaseNamingStrategy : NamingStrategy {
override fun to(name: String): String = name.take(1).toUpperCase(Locale.ROOT) + name.drop(1)
override fun to(name: String): String = name.take(1).uppercase() + name.drop(1)
}

object SnakeCaseNamingStrategy : NamingStrategy {
override fun to(name: String): String = name.fold(StringBuilder()) { sb, c ->
if (c.isUpperCase())
sb.append('_').append(c.toLowerCase())
sb.append('_').append(c.lowercase())
else
sb.append(c.toLowerCase())
sb.append(c.lowercase())
}.toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import java.util.UUID
class DefaultAvroTest : FunSpec({

test("encoding UUID") {

@Serializable
data class Foo(@Contextual val a: UUID)

val uuid = UUID.randomUUID()
val record = Avro.default.toRecord(Foo.serializer(), Foo(uuid))
record.get("a").toString() shouldBe uuid.toString()
}
})
}) {
@Serializable
data class Foo(@Contextual val a: UUID)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ class RecordEncoderTest : FunSpec({

test("encoding basic data class") {

@Serializable
data class Foo(val a: String, val b: Double, val c: Boolean)

// Avro.default.dump(Foo.serializer(), Foo("hello", 123.456, true)) shouldBe ""
}

test("to/from records of sets of ints") {

@Serializable
data class S(val t: Set<Int>)

val r = Avro.default.toRecord(S.serializer(), S(setOf(1)))
val s = Avro.default.fromRecord(S.serializer(), r) // this line fails
s.t shouldBe setOf(1)
}
})
}) {
@Serializable
data class Foo(val a: String, val b: Double, val c: Boolean)

@Serializable
data class S(val t: Set<Int>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ class AvroInlineDecoderTest : FunSpec({

test("decode @AvroInline") {

@Serializable
@AvroInline
data class Name(val value: String)

@Serializable
data class Product(val id: String, val name: Name)

val schema = Avro.default.schema(Product.serializer())
val record = ListRecord(schema, listOf(Utf8("123"), Utf8("sneakers")))
Avro.default.fromRecord(Product.serializer(), record) shouldBe Product("123", Name("sneakers"))
}

})
}) {

@Serializable
@AvroInline
data class Name(val value: String)

@Serializable
data class Product(val id: String, val name: Name)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import org.apache.avro.util.Utf8

class AvroNameDecoderTest : FunSpec({

@Serializable
data class Foo(@AvroName("bar") val foo: String)

test("decoder should take into account @AvroName on fields") {
val schema = Avro.default.schema(Foo.serializer())
val record = GenericData.Record(schema)
record.put("bar", Utf8("hello"))
Avro.default.fromRecord(Foo.serializer(), record) shouldBe Foo("hello")
}
})
}) {

@Serializable
data class Foo(@AvroName("bar") val foo: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ class BigDecimalDecoderTest : FunSpec({

test("decode big decimals from bytes") {

@Serializable
data class Test(val b: BigDecimal)

val logical = LogicalTypes.decimal(8, 2)

val schema = SchemaBuilder.record("Test").fields()
Expand All @@ -39,9 +36,6 @@ class BigDecimalDecoderTest : FunSpec({

test("decode big decimals from strings") {

@Serializable
data class Test(val b: BigDecimal)

val schema = SchemaBuilder.record("Test").fields()
.name("b").type(Schema.create(Schema.Type.STRING)).noDefault()
.endRecord()
Expand All @@ -54,9 +48,6 @@ class BigDecimalDecoderTest : FunSpec({

test("decode big decimals from fixed") {

@Serializable
data class Test(val b: BigDecimal)

val logical = LogicalTypes.decimal(10, 8)

val schema = SchemaBuilder.record("Test").fields()
Expand All @@ -68,4 +59,8 @@ class BigDecimalDecoderTest : FunSpec({

Avro.default.fromRecord(Test.serializer(), record) shouldBe Test(BigDecimal("12345678.00000000"))
}
})
}) {

@Serializable
data class Test(val b: BigDecimal)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ class BigIntegerDecoderTest : FunSpec({

test("decode big integer from string") {

@Serializable
data class Test(val b: BigInteger)

val schema = SchemaBuilder.record("Test").fields().name("b").type(Schema.create(Schema.Type.STRING)).noDefault().endRecord()

val record = GenericData.Record(schema)
record.put("b", Utf8("1927398217318546456532973912379127391279312983719"))

Avro.default.fromRecord(Test.serializer(), record) shouldBe Test(BigInteger("1927398217318546456532973912379127391279312983719"))
}
})
}) {

@Serializable
data class Test(val b: BigInteger)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ import java.nio.ByteBuffer

class ByteArrayDecoderTest : FunSpec({

@Serializable
data class ByteArrayTest(val z: ByteArray)

@Serializable
data class ArrayByteTest(val z: Array<Byte>)

@Serializable
data class ListByteTest(val z: List<Byte>)

test("decode ByteBuffer to ByteArray") {
val schema = Avro.default.schema(ByteArrayTest.serializer())
val record = GenericData.Record(schema)
Expand Down Expand Up @@ -101,4 +92,14 @@ class ByteArrayDecoderTest : FunSpec({
record.put("z", listOf<Byte>(1, 4, 9))
Avro.default.fromRecord(ArrayByteTest.serializer(), record).z shouldBe arrayOf<Byte>(1, 4, 9)
}
})
}) {

@Serializable
data class ByteArrayTest(val z: ByteArray)

@Serializable
data class ArrayByteTest(val z: Array<Byte>)

@Serializable
data class ListByteTest(val z: List<Byte>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,6 @@ import java.time.temporal.ChronoUnit

class DateDecoderTest : FunSpec({

@Serializable
data class WithLocalTime(val z: LocalTime)

@Serializable
data class WithLocalDate(val z: LocalDate)

@Serializable
data class WithLocalDateTime(val z: LocalDateTime)

@Serializable
data class WithTimestamp(val z: Timestamp)

@Serializable
data class WithInstant(@Serializable(with = InstantSerializer::class) val z: Instant)

@Serializable
data class WithInstantAndMicros(@Serializable(with = InstantToMicroSerializer::class) val z: Instant)

test("decode int to LocalTime") {
val schema = Avro.default.schema(WithLocalTime.serializer())
val record = GenericData.Record(schema)
Expand Down Expand Up @@ -88,4 +70,23 @@ class DateDecoderTest : FunSpec({
Avro.default.fromRecord(WithInstantAndMicros.serializer(), record) shouldBe
WithInstantAndMicros(Instant.ofEpochMilli(1538312231000L).plus(100, ChronoUnit.MICROS))
}
})
}) {

@Serializable
data class WithLocalTime(val z: LocalTime)

@Serializable
data class WithLocalDate(val z: LocalDate)

@Serializable
data class WithLocalDateTime(val z: LocalDateTime)

@Serializable
data class WithTimestamp(val z: Timestamp)

@Serializable
data class WithInstant(@Serializable(with = InstantSerializer::class) val z: Instant)

@Serializable
data class WithInstantAndMicros(@Serializable(with = InstantToMicroSerializer::class) val z: Instant)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ import org.apache.avro.generic.GenericData

class FixedDecoderTest : FunSpec({

@Serializable
data class FixedString(val z: String)

@Serializable
data class NullableFixedType(val z: FixedString?)

test("decode bytes to String") {
val schema = SchemaBuilder.record("FixedString").fields()
.name("z").type(Schema.createFixed("z", null, "ns", 10)).noDefault()
Expand All @@ -33,4 +27,11 @@ class FixedDecoderTest : FunSpec({
// FixedValueType("sam")))
// }

})
}) {

@Serializable
data class FixedString(val z: String)

@Serializable
data class NullableFixedType(val z: FixedString?)
}
Loading