Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReactiveMongo KeyReader/Writer #303

Merged
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package enumeratum

import reactivemongo.api.bson._

import scala.util.{Failure, Try}
import scala.util.{Failure, Success, Try}

/**
* Holds BSON reader and writer for [[enumeratum.Enum]]
Expand All @@ -22,63 +22,115 @@ object EnumHandler {
def reader[A <: EnumEntry](
enum: Enum[A],
insensitive: Boolean = false
): BSONReader[A] =
new BSONReader[A] {
override def readTry(bson: BSONValue): Try[A] =
bson match {
case BSONString(s) if insensitive => Try(enum.withNameInsensitive(s))
case BSONString(s) => Try(enum.withName(s))
case _ => Failure(new RuntimeException("String value expected"))
}
}
): BSONReader[A] = {
if (insensitive) collect[A](enum.withNameInsensitiveOption)
else collect[A](enum.withNameOption)
}

/**
* Returns a BSONReader for a given enum [[Enum]] transformed to lower case
* Returns a KeyReader for a given enum [[Enum]]
*
* @param enum The enum
* @param insensitive bind in a case-insensitive way, defaults to false
*/
def readerLowercaseOnly[A <: EnumEntry](enum: Enum[A]): BSONReader[A] = new BSONReader[A] {
override def readTry(bson: BSONValue): Try[A] = bson match {
case BSONString(s) => Try(enum.withNameLowercaseOnly(s))
case _ => Failure(new RuntimeException("String value expected"))
}
def keyReader[A <: EnumEntry](
enum: Enum[A],
insensitive: Boolean = false
): KeyReader[A] = {
if (insensitive) collectKey[A](enum.withNameInsensitiveOption)
else collectKey[A](enum.withNameOption)
}

/**
* Returns a BSONReader for a given enum [[Enum]] transformed to lower case
*
* @param enum The enum
*/
def readerLowercaseOnly[A <: EnumEntry](enum: Enum[A]): BSONReader[A] =
collect[A](enum.withNameLowercaseOnlyOption)

/**
* Returns a KeyReader for a given enum [[Enum]] transformed to lower case
*
* @param enum The enum
*/
def keyReaderLowercaseOnly[A <: EnumEntry](enum: Enum[A]): KeyReader[A] =
collectKey[A](enum.withNameLowercaseOnlyOption)

/**
* Returns a BSONReader for a given enum [[Enum]] transformed to upper case
*
* @param enum The enum
*/
def readerUppercaseOnly[A <: EnumEntry](enum: Enum[A]): BSONReader[A] = new BSONReader[A] {
override def readTry(bson: BSONValue): Try[A] =
bson match {
case BSONString(s) => Try(enum.withNameUppercaseOnly(s))
case _ => Failure(new RuntimeException("String value expected"))
def readerUppercaseOnly[A <: EnumEntry](enum: Enum[A]): BSONReader[A] =
collect[A](enum.withNameUppercaseOnlyOption)

/**
* Returns a KeyReader for a given enum [[Enum]] transformed to upper case
*
* @param enum The enum
*/
def keyReaderUppercaseOnly[A <: EnumEntry](enum: Enum[A]): KeyReader[A] =
collectKey[A](enum.withNameUppercaseOnlyOption)

private def collect[A](f: String => Option[A]): BSONReader[A] =
BSONReader.option[A] {
case BSONString(str) => f(str)
case _ => None
}

private def collectKey[A](f: String => Option[A]): KeyReader[A] =
KeyReader.from[A] { key =>
f(key) match {
case Some(a) => Success(a)
case _ => Failure(exceptions.TypeDoesNotMatchException(key, "key"))
}
}
}

/**
* Returns a BSONWriter for a given enum [[Enum]]
*/
def writer[A <: EnumEntry](enum: Enum[A]): BSONWriter[A] = new BSONWriter[A] {
override def writeTry(t: A): Try[BSONValue] = Try(BSONString(t.entryName))
}
def writer[A <: EnumEntry](enum: Enum[A]): BSONWriter[A] =
BSONWriter[A] { t =>
BSONString(t.entryName)
}

/**
* Returns a KeyWriter for a given enum [[Enum]]
*/
def keyWriter[A <: EnumEntry](enum: Enum[A]): KeyWriter[A] =
KeyWriter[A](_.entryName)

/**
* Returns a BSONWriter for a given enum [[Enum]], outputting the value as lower case
*/
def writerLowercase[A <: EnumEntry](enum: Enum[A]): BSONWriter[A] = new BSONWriter[A] {
override def writeTry(t: A): Try[BSONValue] = Try(BSONString(t.entryName.toLowerCase))
}
def writerLowercase[A <: EnumEntry](enum: Enum[A]): BSONWriter[A] =
BSONWriter[A] { t =>
BSONString(t.entryName.toLowerCase)
}

/**
* Returns a KeyWriter for a given enum [[Enum]],
* outputting the value as lower case
*/
def keyWriterLowercase[A <: EnumEntry](enum: Enum[A]): KeyWriter[A] =
KeyWriter[A](_.entryName.toLowerCase)

/**
* Returns a BSONWriter for a given enum [[Enum]], outputting the value as upper case
*/
def writerUppercase[A <: EnumEntry](enum: Enum[A]): BSONWriter[A] =
new BSONWriter[A] {
override def writeTry(t: A): Try[BSONValue] = Try(BSONString(t.entryName.toUpperCase))
BSONWriter[A] { t =>
BSONString(t.entryName.toUpperCase)
}

/**
* Returns a KeyWriter for a given enum [[Enum]],
* outputting the value as upper case
*/
def keyWriterUppercase[A <: EnumEntry](enum: Enum[A]): KeyWriter[A] =
KeyWriter[A](_.entryName.toUpperCase)

/**
* Returns a BSONHandler for a given enum [[Enum]]
*
Expand All @@ -88,43 +140,21 @@ object EnumHandler {
def handler[A <: EnumEntry](
enum: Enum[A],
insensitive: Boolean = false
): BSONHandler[A] =
new BSONHandler[A] {
private val concreteReader = reader(enum, insensitive)
private val concreteWriter = writer(enum)

override def readTry(bson: BSONValue): Try[A] = concreteReader.readTry(bson)

override def writeTry(t: A): Try[BSONValue] = concreteWriter.writeTry(t)
}
): BSONHandler[A] = BSONHandler.provided[A](reader(enum, insensitive), writer(enum))

/**
* Returns a BSONHandler for a given enum [[Enum]], handling a lower case transformation
*
* @param enum The enum
*/
def handlerLowercaseOnly[A <: EnumEntry](enum: Enum[A]): BSONHandler[A] =
new BSONHandler[A] {
private val concreteReader = readerLowercaseOnly(enum)
private val concreteWriter = writerLowercase(enum)

override def readTry(bson: BSONValue): Try[A] = concreteReader.readTry(bson)

override def writeTry(t: A): Try[BSONValue] = concreteWriter.writeTry(t)
}
BSONHandler.provided[A](readerLowercaseOnly(enum), writerLowercase(enum))

/**
* Returns a BSONHandler for a given enum [[Enum]], handling an upper case transformation
*
* @param enum The enum
*/
def handlerUppercaseOnly[A <: EnumEntry](enum: Enum[A]): BSONHandler[A] =
new BSONHandler[A] {
private val concreteReader = readerUppercaseOnly(enum)
private val concreteWriter = writerUppercase(enum)

override def readTry(bson: BSONValue): Try[A] = concreteReader.readTry(bson)

override def writeTry(t: A): Try[BSONValue] = concreteWriter.writeTry(t)
}
BSONHandler.provided[A](readerUppercaseOnly(enum), writerUppercase(enum))
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package enumeratum

import reactivemongo.api.bson.BSONHandler
import reactivemongo.api.bson.{BSONHandler, KeyReader, KeyWriter}

/**
* @author Alessandro Lacava (@lambdista)
Expand All @@ -9,4 +9,7 @@ import reactivemongo.api.bson.BSONHandler
trait ReactiveMongoBsonEnum[A <: EnumEntry] { self: Enum[A] =>
implicit val bsonHandler: BSONHandler[A] =
EnumHandler.handler(this)

implicit val keyReader: KeyReader[A] = EnumHandler.keyReader[A](this)
implicit val keyWriter: KeyWriter[A] = EnumHandler.keyWriter[A](this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import scala.util.{Failure, Try}
* Holds BSONValue to implicits. The ones that come with ReactiveMongo by default are for subclasses like BSONLong,
* but what we want are BSONValue and the Reader/Writer/Handler typeclasses are not covariant.
*/
@deprecated("No longer needed", "ReactiveMongo 1.0.0")
object BSONValueHandlers {

implicit def shortHandler: BSONHandler[Short] = new BSONHandler[Short] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package enumeratum.values

import reactivemongo.api.bson.{BSONHandler, BSONReader, BSONValue, BSONWriter}
import reactivemongo.api.bson._

import scala.util.Try

Expand All @@ -11,42 +11,59 @@ import scala.util.Try
object EnumHandler {

/**
* Returns a BSONReader for the provided ValueEnum based on the given base BSONReader for the Enum's value type
* Returns a BSONReader for the provided ValueEnum based on
* the given base BSONReader for the Enum's value type.
*/
def reader[ValueType, EntryType <: ValueEnumEntry[ValueType]](
enum: ValueEnum[ValueType, EntryType]
)(
implicit baseBsonReader: BSONReader[ValueType]
): BSONReader[EntryType] = new BSONReader[EntryType] {
override def readTry(bson: BSONValue): Try[EntryType] =
baseBsonReader.readTry(bson).map(enum.withValue)
): BSONReader[EntryType] = BSONReader.from[EntryType] { bson =>
baseBsonReader.readTry(bson).map(enum.withValue)
}

/**
* Returns a BSONWriter for the provided ValueEnum based on the given base BSONWriter for the Enum's value type
* Returns a KeyReader for the provided ValueEnum based on
* the given base KeyReader for the Enum's value type.
*/
def keyReader[ValueType, EntryType <: ValueEnumEntry[ValueType]](
enum: ValueEnum[ValueType, EntryType]
)(
implicit baseBsonReader: KeyReader[ValueType]
): KeyReader[EntryType] = KeyReader.from[EntryType] { bson =>
baseBsonReader.readTry(bson).map(enum.withValue)
}

/**
* Returns a BSONWriter for the provided ValueEnum based on
* the given base BSONWriter for the Enum's value type.
*/
def writer[ValueType, EntryType <: ValueEnumEntry[ValueType]](
enum: ValueEnum[ValueType, EntryType]
)(
implicit baseBsonWriter: BSONWriter[ValueType]
): BSONWriter[EntryType] = new BSONWriter[EntryType] {
): BSONWriter[EntryType] = BSONWriter.from[EntryType] { t =>
baseBsonWriter.writeTry(t.value)
}

override def writeTry(t: EntryType): Try[BSONValue] = baseBsonWriter.writeTry(t.value)
/**
* Returns a KeyWriter for the provided ValueEnum based on
* the given base KeyWriter for the Enum's value type.
*/
def keyWriter[ValueType, EntryType <: ValueEnumEntry[ValueType]](
enum: ValueEnum[ValueType, EntryType]
)(
implicit baseBsonWriter: KeyWriter[ValueType]
): KeyWriter[EntryType] = KeyWriter.from[EntryType] { t =>
baseBsonWriter.writeTry(t.value)
}

/**
* Returns a BSONHandler for the provided ValueEnum based on the given base BSONReader and BSONWriter for the
* Enum's value type
* Returns a BSONHandler for the provided ValueEnum based on
* the given base BSONReader and BSONWriter for the Enum's value type.
*/
def handler[ValueType, EntryType <: ValueEnumEntry[ValueType]](
enum: ValueEnum[ValueType, EntryType])(
implicit baseBsonHandler: BSONHandler[ValueType]
): BSONHandler[EntryType] = new BSONHandler[EntryType] {
private val concreteReader = reader(enum)
private val concreteWriter = writer(enum)

override def readTry(bson: BSONValue): Try[EntryType] = concreteReader.readTry(bson)

override def writeTry(t: EntryType): Try[BSONValue] = concreteWriter.writeTry(t)
}
): BSONHandler[EntryType] = BSONHandler.provided[EntryType](reader(enum), writer(enum))
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package enumeratum.values

import enumeratum.values.BSONValueHandlers._
import reactivemongo.api.bson.BSONHandler
import reactivemongo.api.bson._

/**
* @author Alessandro Lacava (@lambdista)
Expand All @@ -14,6 +14,9 @@ sealed trait ReactiveMongoBsonValueEnum[ValueType, EntryType <: ValueEnumEntry[V
* Implicit BSON handler for the entries of this enum
*/
implicit def bsonHandler: BSONHandler[EntryType]

implicit def keyReader: KeyReader[EntryType]
implicit def keyWriter: KeyWriter[EntryType]
}

/**
Expand All @@ -24,6 +27,9 @@ trait IntReactiveMongoBsonValueEnum[EntryType <: IntEnumEntry]

implicit val bsonHandler: BSONHandler[EntryType] =
EnumHandler.handler(this)

implicit def keyReader: KeyReader[EntryType] = EnumHandler.keyReader(this)
implicit def keyWriter: KeyWriter[EntryType] = EnumHandler.keyWriter(this)
}

/**
Expand All @@ -34,6 +40,9 @@ trait LongReactiveMongoBsonValueEnum[EntryType <: LongEnumEntry]

implicit val bsonHandler: BSONHandler[EntryType] =
EnumHandler.handler(this)

implicit def keyReader: KeyReader[EntryType] = EnumHandler.keyReader(this)
implicit def keyWriter: KeyWriter[EntryType] = EnumHandler.keyWriter(this)
}

/**
Expand All @@ -44,6 +53,9 @@ trait ShortReactiveMongoBsonValueEnum[EntryType <: ShortEnumEntry]

implicit val bsonHandler: BSONHandler[EntryType] =
EnumHandler.handler(this)

implicit def keyReader: KeyReader[EntryType] = EnumHandler.keyReader(this)
implicit def keyWriter: KeyWriter[EntryType] = EnumHandler.keyWriter(this)
}

/**
Expand All @@ -54,6 +66,9 @@ trait StringReactiveMongoBsonValueEnum[EntryType <: StringEnumEntry]

implicit val bsonHandler: BSONHandler[EntryType] =
EnumHandler.handler(this)

implicit def keyReader: KeyReader[EntryType] = EnumHandler.keyReader(this)
implicit def keyWriter: KeyWriter[EntryType] = EnumHandler.keyWriter(this)
}

/**
Expand All @@ -64,6 +79,9 @@ trait CharReactiveMongoBsonValueEnum[EntryType <: CharEnumEntry]

implicit val bsonHandler: BSONHandler[EntryType] =
EnumHandler.handler(this)

implicit def keyReader: KeyReader[EntryType] = EnumHandler.keyReader(this)
implicit def keyWriter: KeyWriter[EntryType] = EnumHandler.keyWriter(this)
}

/**
Expand All @@ -74,4 +92,7 @@ trait ByteReactiveMongoBsonValueEnum[EntryType <: ByteEnumEntry]

implicit val bsonHandler: BSONHandler[EntryType] =
EnumHandler.handler(this)

implicit def keyReader: KeyReader[EntryType] = EnumHandler.keyReader(this)
implicit def keyWriter: KeyWriter[EntryType] = EnumHandler.keyWriter(this)
}
Loading