Skip to content

Commit

Permalink
✨ Add KotoolsTypesSerializers.emailAddress property (#635)
Browse files Browse the repository at this point in the history
Adds module for serializing the `EmailAddress` type.
  • Loading branch information
LVMVRQUXL committed Apr 17, 2024
1 parent 8460147 commit 5c64203
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
public final class org/kotools/types/kotlinx/serialization/KotoolsTypesSerializers {
public static final field INSTANCE Lorg/kotools/types/kotlinx/serialization/KotoolsTypesSerializers;
public final fun getAll ()Lkotlinx/serialization/modules/SerializersModule;
public final fun getEmailAddress ()Lkotlinx/serialization/modules/SerializersModule;
public final fun getZero ()Lkotlinx/serialization/modules/SerializersModule;
public final fun toString ()Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.kotools.types.kotlinx.serialization

import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotools.types.experimental.ExperimentalKotoolsTypesApi
import org.kotools.types.EmailAddress

@OptIn(ExperimentalKotoolsTypesApi::class)
internal object EmailAddressAsStringSerializer : KSerializer<EmailAddress> {
override val descriptor: SerialDescriptor
get() {
val serialName: String = serialNameOf<EmailAddress>()
return PrimitiveSerialDescriptor(serialName, PrimitiveKind.STRING)
}

override fun serialize(encoder: Encoder, value: EmailAddress): Unit = value
.toString()
.let(encoder::encodeString)

override fun deserialize(decoder: Decoder): EmailAddress {
val decodedValue: String = decoder.decodeString()
val address: EmailAddress? = EmailAddress.fromStringOrNull(decodedValue)
if (address != null) return address
DeserializationError(deserializer = this, decodedValue)
.fail()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
import kotools.types.experimental.ExperimentalKotoolsTypesApi
import kotools.types.internal.simpleNameOf
import org.kotools.types.EmailAddress
import org.kotools.types.Zero
import org.kotools.types.internal.ExperimentalSince
import org.kotools.types.internal.KotoolsTypesVersion
Expand All @@ -25,6 +26,18 @@ public object KotoolsTypesSerializers {
public val all: SerializersModule
get() = SerializersModule { include(zero) }

/**
* Returns the module for serializing the [EmailAddress] type.
*
* Here's an example of calling this property from Kotlin code:
*
* SAMPLE: KotoolsTypesSerializersKotlinSample.emailAddress.md
*/
public val emailAddress: SerializersModule
get() = SerializersModule {
contextual(EmailAddressAsStringSerializer)
}

/**
* Returns the module for serializing the [Zero] type.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.kotools.types.kotlinx.serialization

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialKind
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotools.types.experimental.ExperimentalKotoolsTypesApi
import org.kotools.types.EmailAddress
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class EmailAddressAsStringSerializerTest {
@OptIn(
ExperimentalKotoolsTypesApi::class,
ExperimentalSerializationApi::class
)
@Test
fun descriptor_serialName_should_be_the_qualified_name_of_EmailAddress() {
val actual: String =
EmailAddressAsStringSerializer.descriptor.serialName
val expected: String = serialNameOf<EmailAddress>()
assertEquals(expected, actual)
}

@OptIn(ExperimentalSerializationApi::class)
@Test
fun descriptor_kind_should_be_PrimitiveKind_STRING() {
val actual: SerialKind = EmailAddressAsStringSerializer.descriptor.kind
val expected: SerialKind = PrimitiveKind.STRING
assertEquals(expected, actual)
}

@OptIn(ExperimentalKotoolsTypesApi::class)
@Test
fun serialization_should_behave_like_for_the_String_type() {
val value = "[email protected]"
val address: EmailAddress = EmailAddress.fromString(value)
val actual: String =
Json.encodeToString(EmailAddressAsStringSerializer, address)
val expected: String = Json.encodeToString(value)
assertEquals(expected, actual)
}

@OptIn(ExperimentalKotoolsTypesApi::class)
@Test
fun deserialization_should_pass_with_a_valid_String() {
val value = "[email protected]"
val encoded: String = Json.encodeToString(value)
val actual: EmailAddress =
Json.decodeFromString(EmailAddressAsStringSerializer, encoded)
val expected: EmailAddress = EmailAddress.fromString(value)
assertEquals(expected, actual)
}

@Test
fun deserialization_should_fail_with_a_String_missing_an_at_sign(): Unit =
this.failingDeserialization(value = "contactKotools.org")

@Test
fun deserialization_should_fail_with_a_String_missing_a_dot_in_domain(): Unit =
this.failingDeserialization(value = "contact@kotoolsOrg")

@Test
fun deserialization_should_fail_with_a_String_having_whitespaces_in_local_part(): Unit =
this.failingDeserialization(value = " cont act @kotools.org")

@Test
fun deserialization_should_fail_with_a_String_having_whitespaces_in_domain_first_label(): Unit =
this.failingDeserialization(value = "contact@ ko tools .org")

@Test
fun deserialization_should_fail_with_a_String_having_whitespaces_in_domain_second_label(): Unit =
this.failingDeserialization(value = "contact@kotools. or g ")

private fun failingDeserialization(value: String) {
val encoded: String = Json.encodeToString(value)
val deserializer = EmailAddressAsStringSerializer
val exception: SerializationException = assertFailsWith {
Json.decodeFromString(deserializer, encoded)
}
val actual: String? = exception.message
val expected: String =
DeserializationError(deserializer, decodedValue = value).message
assertEquals(expected, actual)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.serializer
import kotools.types.experimental.ExperimentalKotoolsTypesApi
import kotools.types.internal.simpleNameOf
import org.kotools.types.EmailAddress
import org.kotools.types.Zero
import kotlin.test.Test
import kotlin.test.assertEquals
Expand All @@ -16,6 +17,12 @@ class KotoolsTypesSerializersTest {
module.serializer<Zero>()
}

@Test
fun emailAddress_should_contain_serializer_for_EmailAddress_type() {
val module: SerializersModule = KotoolsTypesSerializers.emailAddress
module.serializer<EmailAddress>()
}

@Test
fun zero_module_should_contain_serializer_for_Zero_type() {
val module: SerializersModule = KotoolsTypesSerializers.zero
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.kotools.types.kotlinx.serialization
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotools.types.experimental.ExperimentalKotoolsTypesApi
import org.kotools.types.EmailAddress
import org.kotools.types.Zero

@OptIn(ExperimentalKotoolsTypesApi::class)
Expand All @@ -16,6 +17,18 @@ internal object KotoolsTypesSerializersKotlinSample {
println(zero == decoded) // true
} // END

fun emailAddress() {
val format = Json {
serializersModule = KotoolsTypesSerializers.emailAddress // TABS: 1
}
val emailAddress: EmailAddress =
EmailAddress.fromString("[email protected]") // TABS: 1
val encoded: String = format.encodeToString(emailAddress)
println(encoded) // "[email protected]"
val decoded: EmailAddress = format.decodeFromString(encoded)
println(emailAddress == decoded) // true
} // END

fun zero() {
val format = Json { serializersModule = KotoolsTypesSerializers.zero }
val zero = Zero()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ class KotoolsTypesSerializersKotlinSampleTest {
assertContentEquals(expected, actual)
}

@Test
fun `emailAddress should pass`() {
val actual: List<String> = SystemLambda
.tapSystemOut(KotoolsTypesSerializersKotlinSample::emailAddress)
.trim()
.lines()
val expected: List<String> = listOf("\"[email protected]\"", "true")
assertContentEquals(expected, actual)
}

@Test
fun `zero should pass`() {
val actual: List<String> = SystemLambda
Expand Down

0 comments on commit 5c64203

Please sign in to comment.