aboutsummaryrefslogtreecommitdiff
path: root/core-types/src/main/scala/xyz/driver/core/domain.scala
diff options
context:
space:
mode:
authorJakob Odersky <jakob@driver.xyz>2018-09-12 15:56:41 -0700
committerJakob Odersky <jakob@odersky.com>2018-10-09 16:19:39 -0700
commit7a793ffa068fda8f2146f84fa785328d928dba03 (patch)
treed489b0b9ebf30ca61e2b6ef1c9906b704bc1fa1e /core-types/src/main/scala/xyz/driver/core/domain.scala
parent2cef01adfe3ebd3a0fa1e0bbbba7f6388198ba10 (diff)
downloaddriver-core-7a793ffa068fda8f2146f84fa785328d928dba03.tar.gz
driver-core-7a793ffa068fda8f2146f84fa785328d928dba03.tar.bz2
driver-core-7a793ffa068fda8f2146f84fa785328d928dba03.zip
Move core types into core-types project
Note that xyz.driver.core.FutureExtensions was moved to xyz.driver.core.rest as it (only) contained logic that dealt with service exceptions, something that belongs into core-rest and must not be depended upon by core-types. This is a breaking change.
Diffstat (limited to 'core-types/src/main/scala/xyz/driver/core/domain.scala')
-rw-r--r--core-types/src/main/scala/xyz/driver/core/domain.scala73
1 files changed, 73 insertions, 0 deletions
diff --git a/core-types/src/main/scala/xyz/driver/core/domain.scala b/core-types/src/main/scala/xyz/driver/core/domain.scala
new file mode 100644
index 0000000..f3b8337
--- /dev/null
+++ b/core-types/src/main/scala/xyz/driver/core/domain.scala
@@ -0,0 +1,73 @@
+package xyz.driver.core
+
+import com.google.i18n.phonenumbers.PhoneNumberUtil
+import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat
+import scalaz.Equal
+import scalaz.std.string._
+import scalaz.syntax.equal._
+
+import scala.util.Try
+import scala.util.control.NonFatal
+
+object domain {
+
+ final case class Email(username: String, domain: String) {
+
+ def value: String = toString
+
+ override def toString: String = username + "@" + domain
+ }
+
+ object Email {
+ implicit val emailEqual: Equal[Email] = Equal.equal {
+ case (left, right) => left.toString.toLowerCase === right.toString.toLowerCase
+ }
+
+ def parse(emailString: String): Option[Email] = {
+ Some(emailString.split("@")) collect {
+ case Array(username, domain) => Email(username, domain)
+ }
+ }
+ }
+
+ final case class PhoneNumber(countryCode: String, number: String, extension: Option[String] = None) {
+
+ def hasExtension: Boolean = extension.isDefined
+
+ /** This is a more human-friendly alias for #toE164String() */
+ def toCompactString: String = s"+$countryCode$number${extension.fold("")(";ext=" + _)}"
+
+ /** Outputs the phone number in a E.164-compliant way, e.g. +14151234567 */
+ def toE164String: String = toCompactString
+
+ /**
+ * Outputs the phone number in a "readable" way, e.g. "+1 415-123-45-67 ext. 1234"
+ * @throws IllegalStateException if the contents of this object is not a valid phone number
+ */
+ @throws[IllegalStateException]
+ def toHumanReadableString: String =
+ try {
+ val phoneNumber = PhoneNumber.phoneUtil.parse(toE164String, "US")
+ PhoneNumber.phoneUtil.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL)
+ } catch {
+ case NonFatal(e) => throw new IllegalStateException(s"$toString is not a valid number", e)
+ }
+
+ override def toString: String = s"+$countryCode $number${extension.fold("")(" ext. " + _)}"
+ }
+
+ object PhoneNumber {
+
+ private[PhoneNumber] val phoneUtil = PhoneNumberUtil.getInstance()
+
+ def parse(phoneNumber: String): Option[PhoneNumber] = {
+ val validated = Try(phoneUtil.parseAndKeepRawInput(phoneNumber, "US")).toOption.filter(phoneUtil.isValidNumber)
+ validated.map { pn =>
+ PhoneNumber(
+ pn.getCountryCode.toString,
+ pn.getNationalNumber.toString,
+ Option(pn.getExtension).filter(_.nonEmpty))
+ }
+ }
+ }
+}