Skip to content

Commit

Permalink
✨ Add EmailAddress.Companion.fromString(Any) function (#635)
Browse files Browse the repository at this point in the history
Adds this function in the `org.kotools.types` package with tests, tested code samples for Kotlin and Java, and documentation.
Also dumps the Application Binary Interface (ABI).
  • Loading branch information
LVMVRQUXL committed Mar 31, 2024
1 parent 73090a0 commit 74446a3
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 2 deletions.
53 changes: 51 additions & 2 deletions src/commonMain/kotlin/org/kotools/types/EmailAddress.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import kotlin.jvm.JvmStatic
/**
* Represents an [email address](https://en.wikipedia.org/wiki/Email_address).
*
* You can use the [EmailAddress.Companion.fromStringOrNull] function for
* creating an instance of this type.
* You can use the [EmailAddress.Companion.fromString] or the
* [EmailAddress.Companion.fromStringOrNull] functions for creating an instance
* of this type.
*/
@ExperimentalKotoolsTypesApi
@ExperimentalSince(KotoolsTypesVersion.Unreleased)
Expand Down Expand Up @@ -59,6 +60,46 @@ public class EmailAddress private constructor() {
*/
public const val PATTERN: String = "^\\S+@\\S+\\.\\S+\$"

/**
* Creates an instance of [EmailAddress] from the string representation
* of the specified [value], or throws an [IllegalArgumentException] if
* the string representation of [value] doesn't match the
* [default pattern][PATTERN].
*
* <br>
* <details open>
* <summary>
* <b>Calling from Kotlin</b>
* </summary>
*
* Here's an example of calling this function from Kotlin code:
*
* SAMPLE: EmailAddressCompanionKotlinSample.fromStringSample.md
* </details>
*
* <br>
* <details>
* <summary>
* <b>Calling from Java</b>
* </summary>
*
* Here's an example of calling this function from Java code:
*
* SAMPLE: EmailAddressCompanionJavaSample.fromStringSample.md
* </details>
* <br>
*
* You can use the [fromStringOrNull] function for returning `null`
* instead of throwing an exception in case of invalid [value].
*/
@JvmStatic
public fun fromString(value: Any): EmailAddress {
val address: EmailAddress? = fromStringOrNull(value)
return requireNotNull(address) {
InvalidEmailAddress(value).message
}
}

/**
* Creates an instance of [EmailAddress] from the string representation
* of the specified [value], or returns `null` if the string
Expand Down Expand Up @@ -86,6 +127,10 @@ public class EmailAddress private constructor() {
*
* SAMPLE: EmailAddressCompanionJavaSample.fromStringOrNullSample.md
* </details>
* <br>
*
* You can use the [fromString] function for throwing an exception
* instead of returning `null` in case of invalid [value].
*/
@JvmStatic
public fun fromStringOrNull(value: Any): EmailAddress? {
Expand All @@ -95,3 +140,7 @@ public class EmailAddress private constructor() {
}
}
}

internal class InvalidEmailAddress(value: Any) : IllegalArgumentException() {
override val message: String = "\"$value\" is an invalid email address."
}
66 changes: 66 additions & 0 deletions src/commonTest/kotlin/org/kotools/types/EmailAddressTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package org.kotools.types
import kotools.types.experimental.ExperimentalKotoolsTypesApi
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

@OptIn(ExperimentalKotoolsTypesApi::class)
class EmailAddressCompanionTest {
Expand All @@ -15,6 +17,70 @@ class EmailAddressCompanionTest {
assertEquals(expected, actual)
}

@Test
fun fromString_Any_should_pass_with_a_valid_value() {
val value: Any = "[email protected]"
val actual: Result<EmailAddress> = kotlin.runCatching {
EmailAddress.fromString(value)
}
assertTrue(actual.isSuccess)
}

@Test
fun fromString_Any_should_fail_with_a_missing_at_sign_in_value() {
val value: Any = "contactKotools.org"
val exception: IllegalArgumentException = assertFailsWith {
EmailAddress.fromString(value)
}
val actual: String? = exception.message
val expected: String = InvalidEmailAddress(value).message
assertEquals(expected, actual)
}

@Test
fun fromString_Any_should_fail_with_a_missing_dot_in_domain_of_value() {
val value: Any = "contact@kotoolsOrg"
val exception: IllegalArgumentException = assertFailsWith {
EmailAddress.fromString(value)
}
val actual: String? = exception.message
val expected: String = InvalidEmailAddress(value).message
assertEquals(expected, actual)
}

@Test
fun fromString_Any_should_fail_with_whitespaces_in_local_part_of_value() {
val value: Any = " cont act @kotools.org"
val exception: IllegalArgumentException = assertFailsWith {
EmailAddress.fromString(value)
}
val actual: String? = exception.message
val expected: String = InvalidEmailAddress(value).message
assertEquals(expected, actual)
}

@Test
fun fromString_Any_should_fail_with_whitespaces_in_domain_first_label_of_value() {
val value: Any = "contact@ ko tools .org"
val exception: IllegalArgumentException = assertFailsWith {
EmailAddress.fromString(value)
}
val actual: String? = exception.message
val expected: String = InvalidEmailAddress(value).message
assertEquals(expected, actual)
}

@Test
fun fromString_Any_should_fail_with_whitespaces_in_domain_second_label_of_value() {
val value: Any = "contact@kotools. or g "
val exception: IllegalArgumentException = assertFailsWith {
EmailAddress.fromString(value)
}
val actual: String? = exception.message
val expected: String = InvalidEmailAddress(value).message
assertEquals(expected, actual)
}

@Test
fun fromStringOrNull_Any_should_pass_with_a_valid_value() {
val value: Any = "[email protected]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ void patternSample() {
System.out.println(pattern); // ^\S+@\S+\.\S+$
} // END

void fromStringSample() {
final Object value = "[email protected]";
try {
EmailAddress.fromString(value); // TABS: 1
System.out.println("success"); // TABS: 1
} catch (final IllegalArgumentException exception) {
System.out.println("failure"); // TABS: 1
}
// Output: success
} // END

void fromStringOrNullSample() {
final Object value = "[email protected]";
final EmailAddress address = EmailAddress.fromStringOrNull(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ internal object EmailAddressCompanionKotlinSample {
println(pattern) // ^\S+@\S+\.\S+$
} // END

fun fromStringSample() {
val value: Any = "[email protected]"
val result: Result<EmailAddress> = kotlin.runCatching {
EmailAddress.fromString(value) // TABS: 1
}
println(result.isSuccess) // true
} // END

fun fromStringOrNullSample() {
val value: Any = "[email protected]"
val address: EmailAddress? = EmailAddress.fromStringOrNull(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ void patternSample_should_pass() {
Assertions.assertEquals(expected, actual);
}

@Test
void fromStringSample_should_pass() {
final EmailAddressCompanionJavaSample sample =
new EmailAddressCompanionJavaSample();
final String actual = Assertions.assertDoesNotThrow(
() -> SystemLambda.tapSystemOut(sample::fromStringSample)
).trim();
final String expected = "success";
Assertions.assertEquals(expected, actual);
}

@Test
void fromStringOrNullSample_should_pass() {
final EmailAddressCompanionJavaSample sample =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ class EmailAddressCompanionKotlinSampleTest {
assertEquals(expected, actual)
}

@Test
fun fromStringSample_should_pass() {
val actual: Boolean = SystemLambda
.tapSystemOut(EmailAddressCompanionKotlinSample::fromStringSample)
.trim()
.toBooleanStrict()
assertTrue(actual)
}

@Test
fun fromStringOrNullSample_should_pass() {
val actual: Boolean = SystemLambda
Expand Down

0 comments on commit 74446a3

Please sign in to comment.