Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ node_modules

# Ignore compiled assets
/public/assets
.java-version
73 changes: 51 additions & 22 deletions enumeratum-play-json/src/main/scala/enumeratum/EnumFormats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,47 @@ object EnumFormats {
*/
def reads[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A],
insensitive: Boolean = false
insensitive: Boolean = false,
detailedError: Boolean = false
): Reads[A] =
readsAndExtracts[A](e) { s =>
readsAndExtracts[A](e, detailedError) { s =>
if (insensitive) e.withNameInsensitiveOption(s)
else e.withNameOption(s)
}

def readsLowercaseOnly[A <: EnumEntry](@deprecatedName(Symbol("enum")) e: Enum[A]): Reads[A] =
readsAndExtracts[A](e)(e.withNameLowercaseOnlyOption)
def readsLowercaseOnly[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean = false
): Reads[A] =
readsAndExtracts[A](e, detailedError)(e.withNameLowercaseOnlyOption)

def readsUppercaseOnly[A <: EnumEntry](@deprecatedName(Symbol("enum")) e: Enum[A]): Reads[A] =
readsAndExtracts[A](e)(e.withNameUppercaseOnlyOption)
def readsUppercaseOnly[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean = false
): Reads[A] =
readsAndExtracts[A](e, detailedError)(e.withNameUppercaseOnlyOption)

def keyReads[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A],
insensitive: Boolean = false
insensitive: Boolean = false,
detailedError: Boolean = false
): KeyReads[A] =
readsKeyAndExtracts[A](e) { s =>
readsKeyAndExtracts[A](e, detailedError) { s =>
if (insensitive) e.withNameInsensitiveOption(s)
else e.withNameOption(s)
}

def keyReadsLowercaseOnly[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A]
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean = false
): KeyReads[A] =
readsKeyAndExtracts[A](e)(e.withNameLowercaseOnlyOption)
readsKeyAndExtracts[A](e, detailedError)(e.withNameLowercaseOnlyOption)

def keyReadsUppercaseOnly[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A]
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean = false
): KeyReads[A] =
readsKeyAndExtracts[A](e)(e.withNameUppercaseOnlyOption)
readsKeyAndExtracts[A](e, detailedError)(e.withNameUppercaseOnlyOption)

/** Returns a Json writes for a given enum [[Enum]]
*/
Expand Down Expand Up @@ -102,9 +112,10 @@ object EnumFormats {
*/
def formats[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A],
insensitive: Boolean = false
insensitive: Boolean = false,
detailedError: Boolean = false
): Format[A] = {
Format(reads(e, insensitive), writes(e))
Format(reads(e, insensitive, detailedError), writes(e))
}

/** Returns a Json format for a given enum [[Enum]] for handling lower case transformations
Expand All @@ -113,9 +124,10 @@ object EnumFormats {
* The enum
*/
def formatsLowerCaseOnly[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A]
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean = false
): Format[A] = {
Format(readsLowercaseOnly(e), writesLowercaseOnly(e))
Format(readsLowercaseOnly(e, detailedError), writesLowercaseOnly(e))
}

/** Returns a Json format for a given enum [[Enum]] for handling upper case transformations
Expand All @@ -124,31 +136,48 @@ object EnumFormats {
* The enum
*/
def formatsUppercaseOnly[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A]
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean = false
): Format[A] = {
Format(readsUppercaseOnly(e), writesUppercaseOnly(e))
Format(readsUppercaseOnly(e, detailedError), writesUppercaseOnly(e))
}

// ---

private def readsAndExtracts[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A]
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean
)(extract: String => Option[A]): Reads[A] = Reads[A] {
case JsString(s) =>
extract(s) match {
case Some(obj) => JsSuccess(obj)
case None => JsError("error.expected.validenumvalue")
case None if detailedError =>
JsError(
JsonValidationError(
"error.expected.validenumvalue",
s"valid enum values are: (${e.values.map(_.entryName).mkString(", ")}), but provided: $s"
)
)
case None => JsError("error.expected.validenumvalue")
}

case _ => JsError("error.expected.enumstring")
}

private def readsKeyAndExtracts[A <: EnumEntry](
@deprecatedName(Symbol("enum")) e: Enum[A]
@deprecatedName(Symbol("enum")) e: Enum[A],
detailedError: Boolean
)(extract: String => Option[A]): KeyReads[A] = new KeyReads[A] {
def readKey(s: String): JsResult[A] = extract(s) match {
case Some(obj) => JsSuccess(obj)
case None => JsError("error.expected.validenumvalue")
case None if detailedError =>
JsError(
JsonValidationError(
"error.expected.validenumvalue",
s"valid enum values are: (${e.values.map(_.entryName).mkString(", ")}), but provided: $s"
)
)
case None => JsError("error.expected.validenumvalue")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package enumeratum

import play.api.libs.json._

trait PlayDetailedErrorJsonEnum[A <: EnumEntry] { self: Enum[A] =>
implicit val keyWrites: KeyWrites[A] = EnumFormats.keyWrites(this)

implicit def contraKeyWrites[K <: A]: KeyWrites[K] = {
val w = this.keyWrites

new KeyWrites[K] {
def writeKey(k: K) = w.writeKey(k)
}
}

implicit val keyReads: KeyReads[A] = EnumFormats.keyReads(this, detailedError = true)

implicit val jsonFormat: Format[A] = EnumFormats.formats(this, detailedError = true)
implicit def contraJsonWrites[B <: A]: Writes[B] = jsonFormat.contramap[B](b => b: A)
}
8 changes: 8 additions & 0 deletions enumeratum-play-json/src/test/scala/enumeratum/Dummy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@ object UppercaseDummy extends Enum[UppercaseDummy] with PlayUppercaseJsonEnum[Up
case object Cherry extends UppercaseDummy
val values = findValues
}

sealed trait Operation extends EnumEntry
object Operation extends Enum[Operation] with PlayDetailedErrorJsonEnum[Operation] {
val values = findValues
case object Eq extends Operation
case object Add extends Operation
case object Not extends Operation
}
Loading