-
Notifications
You must be signed in to change notification settings - Fork 451
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into arrow-2
- Loading branch information
Showing
28 changed files
with
902 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,3 +82,9 @@ kotlin { | |
} | ||
} | ||
} | ||
|
||
tasks.jar { | ||
manifest { | ||
attributes["Automatic-Module-Name"] = "arrow.atomic" | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
arrow-libs/core/arrow-core-serialization/api/arrow-core-serialization.api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
public final class arrow/core/serialization/EitherSerializer : kotlinx/serialization/KSerializer { | ||
public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V | ||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Larrow/core/Either; | ||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; | ||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; | ||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Larrow/core/Either;)V | ||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V | ||
} | ||
|
||
public final class arrow/core/serialization/IorSerializer : kotlinx/serialization/KSerializer { | ||
public fun <init> (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V | ||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Larrow/core/Ior; | ||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; | ||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; | ||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Larrow/core/Ior;)V | ||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V | ||
} | ||
|
||
public final class arrow/core/serialization/NonEmptyListSerializer : kotlinx/serialization/KSerializer { | ||
public fun <init> (Lkotlinx/serialization/KSerializer;)V | ||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; | ||
public fun deserialize-0-xjo5U (Lkotlinx/serialization/encoding/Decoder;)Ljava/util/List; | ||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; | ||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V | ||
public fun serialize-EgRvm48 (Lkotlinx/serialization/encoding/Encoder;Ljava/util/List;)V | ||
} | ||
|
||
public final class arrow/core/serialization/NonEmptySetSerializer : kotlinx/serialization/KSerializer { | ||
public fun <init> (Lkotlinx/serialization/KSerializer;)V | ||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; | ||
public fun deserialize-J9TPrxk (Lkotlinx/serialization/encoding/Decoder;)Ljava/util/Set; | ||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; | ||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V | ||
public fun serialize-EvCv4gE (Lkotlinx/serialization/encoding/Encoder;Ljava/util/Set;)V | ||
} | ||
|
||
public final class arrow/core/serialization/OptionSerializer : kotlinx/serialization/KSerializer { | ||
public fun <init> (Lkotlinx/serialization/KSerializer;)V | ||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Larrow/core/Option; | ||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; | ||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; | ||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Larrow/core/Option;)V | ||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
@file:Suppress("DSL_SCOPE_VIOLATION") | ||
|
||
plugins { | ||
id(libs.plugins.kotlin.multiplatform.get().pluginId) | ||
alias(libs.plugins.arrowGradleConfig.kotlin) | ||
alias(libs.plugins.arrowGradleConfig.publish) | ||
alias(libs.plugins.arrowGradleConfig.versioning) | ||
alias(libs.plugins.kotest.multiplatform) | ||
id(libs.plugins.kotlinx.serialization.get().pluginId) | ||
} | ||
|
||
apply(from = property("ANIMALSNIFFER_MPP")) | ||
|
||
val enableCompatibilityMetadataVariant = | ||
providers.gradleProperty("kotlin.mpp.enableCompatibilityMetadataVariant") | ||
.orNull?.toBoolean() == true | ||
|
||
if (enableCompatibilityMetadataVariant) { | ||
tasks.withType<Test>().configureEach { | ||
exclude("**/*") | ||
} | ||
} | ||
|
||
kotlin { | ||
sourceSets { | ||
commonMain { | ||
dependencies { | ||
api(projects.arrowCore) | ||
api(libs.kotlin.stdlibCommon) | ||
api(libs.kotlinx.serializationCore) | ||
} | ||
} | ||
if (!enableCompatibilityMetadataVariant) { | ||
commonTest { | ||
dependencies { | ||
implementation(libs.kotlinx.serializationJson) | ||
implementation(libs.kotest.frameworkEngine) | ||
implementation(libs.kotest.assertionsCore) | ||
implementation(libs.kotest.property) | ||
} | ||
} | ||
|
||
jvmTest { | ||
dependencies { | ||
runtimeOnly(libs.kotest.runnerJUnit5) | ||
} | ||
} | ||
} | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
...row-core-serialization/src/commonMain/kotlin/arrow/core/serialization/EitherSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package arrow.core.serialization | ||
|
||
import arrow.core.Either | ||
import arrow.core.None | ||
import arrow.core.Option | ||
import arrow.core.Some | ||
import arrow.core.left | ||
import arrow.core.none | ||
import arrow.core.right | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.SerializationException | ||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor | ||
import kotlinx.serialization.descriptors.SerialDescriptor | ||
import kotlinx.serialization.encoding.CompositeDecoder | ||
import kotlinx.serialization.encoding.Decoder | ||
import kotlinx.serialization.encoding.Encoder | ||
import kotlinx.serialization.encoding.decodeStructure | ||
import kotlinx.serialization.encoding.encodeStructure | ||
|
||
public class EitherSerializer<A, B>( | ||
private val errorSerializer: KSerializer<A>, | ||
private val elementSerializer: KSerializer<B>, | ||
) : KSerializer<Either<A, B>> { | ||
|
||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Either") { | ||
element("left", errorSerializer.descriptor, isOptional = true) | ||
element("right", elementSerializer.descriptor, isOptional = true) | ||
} | ||
override fun serialize(encoder: Encoder, value: Either<A, B>) { | ||
encoder.encodeStructure(descriptor) { | ||
when (value) { | ||
is Either.Left -> encodeSerializableElement(descriptor, 0, errorSerializer, value.value) | ||
is Either.Right -> encodeSerializableElement(descriptor, 1, elementSerializer, value.value) | ||
} | ||
} | ||
} | ||
override fun deserialize(decoder: Decoder): Either<A, B> { | ||
var leftValue: Option<A> = none() | ||
var rightValue: Option<B> = none() | ||
decoder.decodeStructure(descriptor) { | ||
while (true) { | ||
when (val index = decodeElementIndex(descriptor)) { | ||
0 -> { | ||
leftValue = Some(decodeSerializableElement(descriptor, 0, errorSerializer)) | ||
} | ||
1 -> { | ||
rightValue = Some(decodeSerializableElement(descriptor, 1, elementSerializer)) | ||
} | ||
CompositeDecoder.DECODE_DONE -> break | ||
else -> error("unexpected index: $index") | ||
} | ||
} | ||
} | ||
return when { | ||
leftValue is None && rightValue is None -> throw SerializationException("No information found for this Either") | ||
leftValue is Some && rightValue is Some -> throw SerializationException("Both Left and Right specified for Either") | ||
leftValue is Some -> (leftValue as Some<A>).value.left() | ||
rightValue is Some -> (rightValue as Some<B>).value.right() | ||
else -> error("this should never happen") | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
.../arrow-core-serialization/src/commonMain/kotlin/arrow/core/serialization/IorSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package arrow.core.serialization | ||
|
||
import arrow.core.Ior | ||
import arrow.core.None | ||
import arrow.core.Option | ||
import arrow.core.Some | ||
import arrow.core.none | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.SerializationException | ||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor | ||
import kotlinx.serialization.descriptors.SerialDescriptor | ||
import kotlinx.serialization.encoding.CompositeDecoder | ||
import kotlinx.serialization.encoding.Decoder | ||
import kotlinx.serialization.encoding.Encoder | ||
import kotlinx.serialization.encoding.decodeStructure | ||
import kotlinx.serialization.encoding.encodeStructure | ||
|
||
public class IorSerializer<A, B>( | ||
private val errorSerializer: KSerializer<A>, | ||
private val elementSerializer: KSerializer<B>, | ||
) : KSerializer<Ior<A, B>> { | ||
|
||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Ior") { | ||
element("left", errorSerializer.descriptor, isOptional = true) | ||
element("right", elementSerializer.descriptor, isOptional = true) | ||
} | ||
override fun serialize(encoder: Encoder, value: Ior<A, B>) { | ||
encoder.encodeStructure(descriptor) { | ||
when (value) { | ||
is Ior.Left -> encodeSerializableElement(descriptor, 0, errorSerializer, value.value) | ||
is Ior.Right -> encodeSerializableElement(descriptor, 1, elementSerializer, value.value) | ||
is Ior.Both -> { | ||
encodeSerializableElement(descriptor, 0, errorSerializer, value.leftValue) | ||
encodeSerializableElement(descriptor, 1, elementSerializer, value.rightValue) | ||
} | ||
} | ||
} | ||
} | ||
override fun deserialize(decoder: Decoder): Ior<A, B> { | ||
var leftValue: Option<A> = none() | ||
var rightValue: Option<B> = none() | ||
decoder.decodeStructure(descriptor) { | ||
while (true) { | ||
when (val index = decodeElementIndex(descriptor)) { | ||
0 -> { | ||
leftValue = Some(decodeSerializableElement(descriptor, 0, errorSerializer)) | ||
} | ||
1 -> { | ||
rightValue = Some(decodeSerializableElement(descriptor, 1, elementSerializer)) | ||
} | ||
CompositeDecoder.DECODE_DONE -> break | ||
else -> error("unexpected index: $index") | ||
} | ||
} | ||
} | ||
return when { | ||
leftValue is None && rightValue is None -> throw SerializationException("No information found for this Ior") | ||
leftValue is Some && rightValue is Some -> Ior.Both((leftValue as Some<A>).value, (rightValue as Some<B>).value) | ||
leftValue is Some -> Ior.Left((leftValue as Some<A>).value) | ||
rightValue is Some -> Ior.Right((rightValue as Some<B>).value) | ||
else -> error("this should never happen") | ||
} | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...alization/src/commonMain/kotlin/arrow/core/serialization/NonEmptyCollectionSerializers.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package arrow.core.serialization | ||
|
||
import arrow.core.NonEmptyList | ||
import arrow.core.NonEmptySet | ||
import arrow.core.toNonEmptyListOrNull | ||
import arrow.core.toNonEmptySetOrNull | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.SerializationException | ||
import kotlinx.serialization.builtins.ListSerializer | ||
import kotlinx.serialization.builtins.SetSerializer | ||
import kotlinx.serialization.descriptors.SerialDescriptor | ||
import kotlinx.serialization.encoding.Decoder | ||
import kotlinx.serialization.encoding.Encoder | ||
|
||
public class NonEmptyListSerializer<T>( | ||
elementSerializer: KSerializer<T> | ||
) : KSerializer<NonEmptyList<T>> { | ||
private val listSerializer: KSerializer<List<T>> = ListSerializer(elementSerializer) | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
override val descriptor: SerialDescriptor = | ||
SerialDescriptor("NonEmptyList", listSerializer.descriptor) | ||
override fun serialize(encoder: Encoder, value: NonEmptyList<T>) { | ||
listSerializer.serialize(encoder, value.toList()) | ||
} | ||
override fun deserialize(decoder: Decoder): NonEmptyList<T> = | ||
listSerializer.deserialize(decoder).toNonEmptyListOrNull() | ||
?: throw SerializationException("expected non-empty list") | ||
} | ||
|
||
public class NonEmptySetSerializer<T>( | ||
elementSerializer: KSerializer<T> | ||
) : KSerializer<NonEmptySet<T>> { | ||
private val setSerializer: KSerializer<Set<T>> = SetSerializer(elementSerializer) | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
override val descriptor: SerialDescriptor = | ||
SerialDescriptor("NonEmptySet", setSerializer.descriptor) | ||
override fun serialize(encoder: Encoder, value: NonEmptySet<T>) { | ||
setSerializer.serialize(encoder, value.toSet()) | ||
} | ||
override fun deserialize(decoder: Decoder): NonEmptySet<T> = | ||
setSerializer.deserialize(decoder).toNonEmptySetOrNull() | ||
?: throw SerializationException("expected non-empty set") | ||
} |
22 changes: 22 additions & 0 deletions
22
...row-core-serialization/src/commonMain/kotlin/arrow/core/serialization/OptionSerializer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package arrow.core.serialization | ||
|
||
import arrow.core.Option | ||
import arrow.core.toOption | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.builtins.nullable | ||
import kotlinx.serialization.descriptors.SerialDescriptor | ||
import kotlinx.serialization.encoding.Decoder | ||
import kotlinx.serialization.encoding.Encoder | ||
|
||
public class OptionSerializer<T : Any>( | ||
elementSerializer: KSerializer<T> | ||
) : KSerializer<Option<T>> { | ||
private val nullableSerializer: KSerializer<T?> = elementSerializer.nullable | ||
|
||
override val descriptor: SerialDescriptor = nullableSerializer.descriptor | ||
override fun serialize(encoder: Encoder, value: Option<T>) { | ||
nullableSerializer.serialize(encoder, value.getOrNull()) | ||
} | ||
override fun deserialize(decoder: Decoder): Option<T> = | ||
nullableSerializer.deserialize(decoder).toOption() | ||
} |
69 changes: 69 additions & 0 deletions
69
.../arrow-core-serialization/src/commonTest/kotlin/arrow/core/serialization/BackAgainTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
@file:UseSerializers( | ||
EitherSerializer::class, | ||
IorSerializer::class, | ||
OptionSerializer::class, | ||
NonEmptyListSerializer::class, | ||
NonEmptySetSerializer::class | ||
) | ||
|
||
package arrow.core.serialization | ||
|
||
import arrow.core.Either | ||
import arrow.core.Ior | ||
import arrow.core.NonEmptyList | ||
import arrow.core.NonEmptySet | ||
import arrow.core.Option | ||
import io.kotest.core.spec.style.StringSpec | ||
import io.kotest.property.Arb | ||
import io.kotest.property.checkAll | ||
import kotlinx.serialization.UseSerializers | ||
import kotlinx.serialization.json.Json | ||
import kotlinx.serialization.json.encodeToJsonElement | ||
import kotlinx.serialization.json.decodeFromJsonElement | ||
import io.kotest.matchers.shouldBe | ||
import io.kotest.property.arbitrary.int | ||
import io.kotest.property.arbitrary.map | ||
import io.kotest.property.arbitrary.string | ||
import kotlinx.serialization.Serializable | ||
|
||
/* | ||
These types are needed to "trick" the kotlinx.serialization plug-in | ||
to use the corresponding (de)serializers for those types. | ||
*/ | ||
|
||
@Serializable | ||
data class EitherInside<A, B>(val thing: Either<A, B>) | ||
|
||
@Serializable | ||
data class IorInside<A, B>(val thing: Ior<A, B>) | ||
|
||
@Serializable | ||
data class OptionInside<A>(val thing: Option<A>) | ||
|
||
@Serializable | ||
data class NonEmptyListInside<A>(val thing: NonEmptyList<A>) | ||
|
||
@Serializable | ||
data class NonEmptySetInside<A>(val thing: NonEmptySet<A>) | ||
|
||
inline fun <reified T> StringSpec.backAgain(generator: Arb<T>) { | ||
"there and back again, ${T::class.simpleName}" { | ||
checkAll(generator) { e -> | ||
val result = Json.encodeToJsonElement<T>(e) | ||
val back = Json.decodeFromJsonElement<T>(result) | ||
back shouldBe e | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Checks that the result of serializing a value into JSON, | ||
* and then deserializing it, gives back the original. | ||
*/ | ||
class BackAgainTest : StringSpec({ | ||
backAgain(Arb.either(Arb.string(), Arb.int()).map(::EitherInside)) | ||
backAgain(Arb.ior(Arb.string(), Arb.int()).map(::IorInside)) | ||
backAgain(Arb.option(Arb.string()).map(::OptionInside)) | ||
backAgain(Arb.nonEmptyList(Arb.int()).map(::NonEmptyListInside)) | ||
backAgain(Arb.nonEmptySet(Arb.int()).map(::NonEmptySetInside)) | ||
}) |
Oops, something went wrong.