diff --git a/README.md b/README.md index 97b74faa..dc9e7c2c 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ Greeting.withName("SHOUT_GOOD_BYE") ### ValueEnum Asides from enumerations that resolve members from `String` _names_, Enumeratum also supports `ValueEnum`s, enums that resolve -members from simple _values_ like `Int`, `Long`, `Short`, and `String` (without support for runtime transformations). +members from simple _values_ like `Int`, `Long`, `Short`, `Char`, `Byte`, and `String` (without support for runtime transformations). These enums are not modelled after `Enumeration` from standard lib, and therefore have the added ability to make sure, at compile-time, that multiple members do not share the same value. diff --git a/enumeratum-circe/src/main/scala/enumeratum/values/CirceValueEnum.scala b/enumeratum-circe/src/main/scala/enumeratum/values/CirceValueEnum.scala index c10f595a..3cdb5d1b 100644 --- a/enumeratum-circe/src/main/scala/enumeratum/values/CirceValueEnum.scala +++ b/enumeratum-circe/src/main/scala/enumeratum/values/CirceValueEnum.scala @@ -53,3 +53,19 @@ trait StringCirceEnum[EntryType <: StringEnumEntry] extends CirceValueEnum[Strin implicit val circeEncoder = Circe.encoder(this) implicit val circeDecoder = Circe.decoder(this) } + +/** + * CirceEnum for CharEnumEntry + */ +trait CharCirceEnum[EntryType <: CharEnumEntry] extends CirceValueEnum[Char, EntryType] { this: ValueEnum[Char, EntryType] => + implicit val circeEncoder = Circe.encoder(this) + implicit val circeDecoder = Circe.decoder(this) +} + +/** + * CirceEnum for ByteEnumEntry + */ +trait ByteCirceEnum[EntryType <: ByteEnumEntry] extends CirceValueEnum[Byte, EntryType] { this: ValueEnum[Byte, EntryType] => + implicit val circeEncoder = Circe.encoder(this) + implicit val circeDecoder = Circe.decoder(this) +} diff --git a/enumeratum-circe/src/test/scala/enumeratum/values/CirceValueEnumSpec.scala b/enumeratum-circe/src/test/scala/enumeratum/values/CirceValueEnumSpec.scala index 45fd4791..7c171db9 100644 --- a/enumeratum-circe/src/test/scala/enumeratum/values/CirceValueEnumSpec.scala +++ b/enumeratum-circe/src/test/scala/enumeratum/values/CirceValueEnumSpec.scala @@ -16,6 +16,8 @@ class CirceValueEnumSpec extends FunSpec with Matchers { testCirceEnum("ShortCirceEnum", CirceDrinks) testCirceEnum("IntCirceEnum", CirceLibraryItem) testCirceEnum("StringCirceEnum", CirceOperatingSystem) + testCirceEnum("CharEnum", CirceAlphabet) + testCirceEnum("ByteEnum", CirceBites) testCirceEnum("IntCirceEnum with val value members", CirceMovieGenre) // Test method that generates tests for most primitve-based ValueEnums when given a simple descriptor and the enum @@ -124,3 +126,27 @@ case object CirceMovieGenre extends IntEnum[CirceMovieGenre] with IntCirceEnum[C val values = findValues } + +sealed abstract class CirceAlphabet(val value: Char) extends CharEnumEntry + +case object CirceAlphabet extends CharEnum[CirceAlphabet] with CharCirceEnum[CirceAlphabet] { + + case object A extends CirceAlphabet('A') + case object B extends CirceAlphabet('B') + case object C extends CirceAlphabet('C') + case object D extends CirceAlphabet('D') + + val values = findValues + +} + +sealed abstract class CirceBites(val value: Byte) extends ByteEnumEntry + +object CirceBites extends ByteEnum[CirceBites] with ByteCirceEnum[CirceBites] { + val values = findValues + + case object OneByte extends CirceBites(1) + case object TwoByte extends CirceBites(2) + case object ThreeByte extends CirceBites(3) + case object FourByte extends CirceBites(4) +} diff --git a/enumeratum-core/src/main/scala/enumeratum/values/ValueEnum.scala b/enumeratum-core/src/main/scala/enumeratum/values/ValueEnum.scala index 3547c5a3..bf9fd7c8 100644 --- a/enumeratum-core/src/main/scala/enumeratum/values/ValueEnum.scala +++ b/enumeratum-core/src/main/scala/enumeratum/values/ValueEnum.scala @@ -130,7 +130,7 @@ object StringEnum { } /** - * Value enum with [[ShortEnumEntry]] entries + * Value enum with [[StringEnumEntry]] entries * * This is similar to [[enumeratum.Enum]], but different in that values must be * literal values. This restraint allows us to enforce uniqueness at compile time. @@ -148,3 +148,59 @@ trait StringEnum[A <: StringEnumEntry] extends ValueEnum[String, A] { final protected def findValues: IndexedSeq[A] = macro ValueEnumMacros.findStringValueEntriesImpl[A] } +object ByteEnum { + + /** + * Materializes a ByteEnum for an in-scope ByteEnumEntry + */ + implicit def materialiseByteValueEnum[EntryType <: ByteEnumEntry]: ByteEnum[EntryType] = macro EnumMacros.materializeEnumImpl[EntryType] + +} + +/** + * Value enum with [[ByteEnumEntry]] entries + * + * This is similar to [[enumeratum.Enum]], but different in that values must be + * literal values. This restraint allows us to enforce uniqueness at compile time. + * + * Note that uniqueness is only guaranteed if you do not do any runtime string manipulation on values. + */ +trait ByteEnum[A <: ByteEnumEntry] extends ValueEnum[Byte, A] { + + /** + * Method that returns a Seq of [[A]] objects that the macro was able to find. + * + * You will want to use this in some way to implement your [[values]] method. In fact, + * if you aren't using this method...why are you even bothering with this lib? + */ + final protected def findValues: IndexedSeq[A] = macro ValueEnumMacros.findByteValueEntriesImpl[A] +} + +object CharEnum { + + /** + * Materializes a CharEnum for an in-scope CharEnumEntry + */ + implicit def materialiseCharValueEnum[EntryType <: CharEnumEntry]: CharEnum[EntryType] = macro EnumMacros.materializeEnumImpl[EntryType] + +} + +/** + * Value enum with [[CharEnumEntry]] entries + * + * This is similar to [[enumeratum.Enum]], but different in that values must be + * literal values. This restraint allows us to enforce uniqueness at compile time. + * + * Note that uniqueness is only guaranteed if you do not do any runtime string manipulation on values. + */ +trait CharEnum[A <: CharEnumEntry] extends ValueEnum[Char, A] { + + /** + * Method that returns a Seq of [[A]] objects that the macro was able to find. + * + * You will want to use this in some way to implement your [[values]] method. In fact, + * if you aren't using this method...why are you even bothering with this lib? + */ + final protected def findValues: IndexedSeq[A] = macro ValueEnumMacros.findCharValueEntriesImpl[A] +} + diff --git a/enumeratum-core/src/main/scala/enumeratum/values/ValueEnumEntry.scala b/enumeratum-core/src/main/scala/enumeratum/values/ValueEnumEntry.scala index de6fdaf5..b43367ab 100644 --- a/enumeratum-core/src/main/scala/enumeratum/values/ValueEnumEntry.scala +++ b/enumeratum-core/src/main/scala/enumeratum/values/ValueEnumEntry.scala @@ -64,3 +64,12 @@ abstract class ShortEnumEntry extends ValueEnumEntry[Short] */ abstract class StringEnumEntry extends ValueEnumEntry[String] +/** + * Value Enum Entry parent class for [[Byte]] valued entries + */ +abstract class ByteEnumEntry extends ValueEnumEntry[Byte] + +/** + * Value Enum Entry parent class for [[Char]] valued entries + */ +abstract class CharEnumEntry extends ValueEnumEntry[Char] \ No newline at end of file diff --git a/enumeratum-core/src/test/scala/enumeratum/values/Alphabet.scala b/enumeratum-core/src/test/scala/enumeratum/values/Alphabet.scala new file mode 100644 index 00000000..68701a29 --- /dev/null +++ b/enumeratum-core/src/test/scala/enumeratum/values/Alphabet.scala @@ -0,0 +1,19 @@ +package enumeratum.values + +/** + * Created by Lloyd on 9/24/16. + * + * Copyright 2016 + */ +sealed abstract class Alphabet(val value: Char) extends CharEnumEntry + +case object Alphabet extends CharEnum[Alphabet] { + + val values = findValues + + case object A extends Alphabet('A') + case object B extends Alphabet('B') + case object C extends Alphabet('C') + case object D extends Alphabet('D') + +} diff --git a/enumeratum-core/src/test/scala/enumeratum/values/Bites.scala b/enumeratum-core/src/test/scala/enumeratum/values/Bites.scala new file mode 100644 index 00000000..1a204a96 --- /dev/null +++ b/enumeratum-core/src/test/scala/enumeratum/values/Bites.scala @@ -0,0 +1,17 @@ +package enumeratum.values + +/** + * Created by Lloyd on 9/24/16. + * + * Copyright 2016 + */ +sealed abstract class Bites(val value: Byte) extends ByteEnumEntry + +object Bites extends ByteEnum[Bites] { + val values = findValues + + case object OneByte extends Bites(1) + case object TwoByte extends Bites(2) + case object ThreeByte extends Bites(3) + case object FourByte extends Bites(4) +} diff --git a/enumeratum-core/src/test/scala/enumeratum/values/ValueEnumSpec.scala b/enumeratum-core/src/test/scala/enumeratum/values/ValueEnumSpec.scala index f433c7ad..7542b837 100644 --- a/enumeratum-core/src/test/scala/enumeratum/values/ValueEnumSpec.scala +++ b/enumeratum-core/src/test/scala/enumeratum/values/ValueEnumSpec.scala @@ -24,6 +24,8 @@ class ValueEnumSpec extends FunSpec with Matchers with ValueEnumHelpers { testNumericEnum("ShortEnum", Drinks) testNumericEnum("LongEnum", ContentType) testEnum("StringEnum", OperatingSystem, Seq("windows-phone")) + testEnum("CharEnum", Alphabet, Seq('Z')) + testEnum("ByteEnum", Bites, Seq(10).map(_.toByte)) testNumericEnum("when using val members in the body", MovieGenre) testNumericEnum("LongEnum that is nesting an IntEnum", Animal) testNumericEnum("IntEnum that is nested inside a LongEnum", Animal.Mammalian) diff --git a/enumeratum-play-json/src/main/scala/enumeratum/values/EnumFormats.scala b/enumeratum-play-json/src/main/scala/enumeratum/values/EnumFormats.scala index 183d1066..a6e08f7f 100644 --- a/enumeratum-play-json/src/main/scala/enumeratum/values/EnumFormats.scala +++ b/enumeratum-play-json/src/main/scala/enumeratum/values/EnumFormats.scala @@ -36,4 +36,16 @@ object EnumFormats { Format(reads(enum), writes(enum)) } + /** + * Format for Char + */ + implicit val charFormat: Format[Char] = new Format[Char] { + def writes(o: Char): JsValue = JsString(s"$o") + + def reads(json: JsValue): JsResult[Char] = json match { + case JsString(s) if s.length == 1 => JsSuccess(s.charAt(0)) + case _ => JsError("error.expected.singleChar") + } + } + } \ No newline at end of file diff --git a/enumeratum-play-json/src/main/scala/enumeratum/values/PlayJsonValueEnum.scala b/enumeratum-play-json/src/main/scala/enumeratum/values/PlayJsonValueEnum.scala index 26cb1779..ce03f8a3 100644 --- a/enumeratum-play-json/src/main/scala/enumeratum/values/PlayJsonValueEnum.scala +++ b/enumeratum-play-json/src/main/scala/enumeratum/values/PlayJsonValueEnum.scala @@ -1,6 +1,7 @@ package enumeratum.values -import play.api.libs.json.Format +import play.api.libs.json._ +import EnumFormats.charFormat /** * Created by Lloyd on 4/13/16. @@ -43,4 +44,18 @@ trait ShortPlayJsonValueEnum[EntryType <: ShortEnumEntry] extends PlayJsonValueE */ trait StringPlayJsonValueEnum[EntryType <: StringEnumEntry] extends PlayJsonValueEnum[String, EntryType] { this: StringEnum[EntryType] => implicit val format: Format[EntryType] = EnumFormats.formats(this) +} + +/** + * Enum implementation for Char enum members that contains an implicit Play JSON Format + */ +trait CharPlayJsonValueEnum[EntryType <: CharEnumEntry] extends PlayJsonValueEnum[Char, EntryType] { this: CharEnum[EntryType] => + implicit val format: Format[EntryType] = EnumFormats.formats(this) +} + +/** + * Enum implementation for Byte enum members that contains an implicit Play JSON Format + */ +trait BytePlayJsonValueEnum[EntryType <: ByteEnumEntry] extends PlayJsonValueEnum[Byte, EntryType] { this: ByteEnum[EntryType] => + implicit val format: Format[EntryType] = EnumFormats.formats(this) } \ No newline at end of file diff --git a/enumeratum-play-json/src/test/scala/enumeratum/values/EnumFormatsSpec.scala b/enumeratum-play-json/src/test/scala/enumeratum/values/EnumFormatsSpec.scala index f470c660..4616b919 100644 --- a/enumeratum-play-json/src/test/scala/enumeratum/values/EnumFormatsSpec.scala +++ b/enumeratum-play-json/src/test/scala/enumeratum/values/EnumFormatsSpec.scala @@ -1,7 +1,8 @@ package enumeratum.values import org.scalatest._ -import play.api.libs.json.JsString +import EnumFormats._ +import play.api.libs.json.{ JsNumber, JsString } /** * Created by Lloyd on 4/13/16. @@ -16,6 +17,8 @@ class EnumFormatsSpec extends FunSpec with Matchers with EnumJsonFormatHelpers { testNumericReads("LongEnum", ContentType) testNumericReads("ShortEnum", Drinks) testReads("StringEnum", OperatingSystem, JsString) + testReads("CharEnum", Alphabet, { c: Char => JsString(s"$c") }) + testReads("ByteEnum", Bites, { b: Byte => JsNumber(b) }) } @@ -25,6 +28,8 @@ class EnumFormatsSpec extends FunSpec with Matchers with EnumJsonFormatHelpers { testNumericWrites("LongEnum", ContentType) testNumericWrites("ShortEnum", Drinks) testWrites("StringEnum", OperatingSystem, JsString) + testWrites("CharEnum", Alphabet, { c: Char => JsString(s"$c") }) + testWrites("ByteEnum", Bites, { b: Byte => JsNumber(b) }) } @@ -34,6 +39,7 @@ class EnumFormatsSpec extends FunSpec with Matchers with EnumJsonFormatHelpers { testNumericFormats("LongEnum", ContentType) testNumericFormats("ShortEnum", Drinks) testFormats("StringEnum", OperatingSystem, JsString) + testFormats("ByteEnum", Bites, { b: Byte => JsNumber(b.toInt) }) testNumericFormats("PlayJsonValueEnum", JsonDrinks, Some(JsonDrinks.format)) } diff --git a/enumeratum-play/src/main/scala/enumeratum/values/Forms.scala b/enumeratum-play/src/main/scala/enumeratum/values/Forms.scala index 021ce920..0949d946 100644 --- a/enumeratum-play/src/main/scala/enumeratum/values/Forms.scala +++ b/enumeratum-play/src/main/scala/enumeratum/values/Forms.scala @@ -31,4 +31,14 @@ object Forms { } } + /** + * Taken from Play 2.4.x implementation + */ + private[values] val charFormatter: Formatter[Char] = new Formatter[Char] { + def bind(key: String, data: Map[String, String]) = + data.get(key).filter(s => s.length == 1 && s != " ").map(s => Right(s.charAt(0))).getOrElse( + Left(Seq(FormError(key, "error.required", Nil))) + ) + def unbind(key: String, value: Char) = Map(key -> value.toString) + } } diff --git a/enumeratum-play/src/main/scala/enumeratum/values/PlayFormValueEnum.scala b/enumeratum-play/src/main/scala/enumeratum/values/PlayFormValueEnum.scala index bed2a603..83c69a6c 100644 --- a/enumeratum-play/src/main/scala/enumeratum/values/PlayFormValueEnum.scala +++ b/enumeratum-play/src/main/scala/enumeratum/values/PlayFormValueEnum.scala @@ -1,7 +1,7 @@ package enumeratum.values -import play.api.data.format.{ Formatter, Formats } -import play.api.data.Mapping +import play.api.data.format.{ Formats, Formatter } +import play.api.data.{ FormError, Mapping } /** * Created by Lloyd on 4/13/16. @@ -51,4 +51,18 @@ trait ShortPlayFormValueEnum[EntryType <: ShortEnumEntry] extends PlayFormValueE */ trait StringPlayFormValueEnum[EntryType <: StringEnumEntry] extends PlayFormValueEnum[String, EntryType] { this: StringEnum[EntryType] => protected val baseFormatter: Formatter[String] = Formats.stringFormat +} + +/** + * Form Bindable implicits for CharEnum + */ +trait CharPlayFormValueEnum[EntryType <: CharEnumEntry] extends PlayFormValueEnum[Char, EntryType] { this: CharEnum[EntryType] => + protected val baseFormatter: Formatter[Char] = Forms.charFormatter +} + +/** + * Form Bindable implicits for ByteEnum + */ +trait BytePlayFormValueEnum[EntryType <: ByteEnumEntry] extends PlayFormValueEnum[Byte, EntryType] { this: ByteEnum[EntryType] => + protected val baseFormatter: Formatter[Byte] = Formats.byteFormat } \ No newline at end of file diff --git a/enumeratum-play/src/main/scala/enumeratum/values/PlayPathBindableValueEnum.scala b/enumeratum-play/src/main/scala/enumeratum/values/PlayPathBindableValueEnum.scala index dce454c1..b80eb35e 100644 --- a/enumeratum-play/src/main/scala/enumeratum/values/PlayPathBindableValueEnum.scala +++ b/enumeratum-play/src/main/scala/enumeratum/values/PlayPathBindableValueEnum.scala @@ -61,4 +61,18 @@ trait ShortPlayPathBindableValueEnum[EntryType <: ShortEnumEntry] extends PlayPa */ trait StringPlayPathBindableValueEnum[EntryType <: StringEnumEntry] extends PlayPathBindableValueEnum[String, EntryType] { this: StringEnum[EntryType] => implicit val pathBindable: PathBindable[EntryType] = UrlBinders.pathBinder(this)(PathBindable.bindableString) +} + +/** + * Path Bindable implicits for CharEnum + */ +trait CharPlayPathBindableValueEnum[EntryType <: CharEnumEntry] extends PlayPathBindableValueEnum[Char, EntryType] { this: CharEnum[EntryType] => + implicit val pathBindable: PathBindable[EntryType] = UrlBinders.pathBinder(this)(PathBindable.bindableChar) +} + +/** + * Path Bindable implicits for ByteEnum + */ +trait BytePlayPathBindableValueEnum[EntryType <: ByteEnumEntry] extends PlayPathBindableValueEnum[Byte, EntryType] { this: ByteEnum[EntryType] => + implicit val pathBindable: PathBindable[EntryType] = UrlBinders.pathBinder(this)(PathBindable.bindableInt.transform(_.toByte, _.toInt)) } \ No newline at end of file diff --git a/enumeratum-play/src/main/scala/enumeratum/values/PlayQueryBindableValueEnum.scala b/enumeratum-play/src/main/scala/enumeratum/values/PlayQueryBindableValueEnum.scala index e58c9bdf..55775d3e 100644 --- a/enumeratum-play/src/main/scala/enumeratum/values/PlayQueryBindableValueEnum.scala +++ b/enumeratum-play/src/main/scala/enumeratum/values/PlayQueryBindableValueEnum.scala @@ -43,4 +43,18 @@ trait ShortPlayQueryBindableValueEnum[EntryType <: ShortEnumEntry] extends PlayQ */ trait StringPlayQueryBindableValueEnum[EntryType <: StringEnumEntry] extends PlayQueryBindableValueEnum[String, EntryType] { this: StringEnum[EntryType] => implicit val queryBindable: QueryStringBindable[EntryType] = UrlBinders.queryBinder(this)(QueryStringBindable.bindableString) +} + +/** + * Query Bindable implicits for CharEnum + */ +trait CharPlayQueryBindableValueEnum[EntryType <: CharEnumEntry] extends PlayQueryBindableValueEnum[Char, EntryType] { this: CharEnum[EntryType] => + implicit val queryBindable: QueryStringBindable[EntryType] = UrlBinders.queryBinder(this)(QueryStringBindable.bindableChar) +} + +/** + * Query Bindable implicits for ByteEnum + */ +trait BytePlayQueryBindableValueEnum[EntryType <: ByteEnumEntry] extends PlayQueryBindableValueEnum[Byte, EntryType] { this: ByteEnum[EntryType] => + implicit val queryBindable: QueryStringBindable[EntryType] = UrlBinders.queryBinder(this)(QueryStringBindable.bindableInt.transform(_.toByte, _.toInt)) } \ No newline at end of file diff --git a/enumeratum-play/src/main/scala/enumeratum/values/PlayValueEnums.scala b/enumeratum-play/src/main/scala/enumeratum/values/PlayValueEnums.scala index ab56c7e4..d8d63066 100644 --- a/enumeratum-play/src/main/scala/enumeratum/values/PlayValueEnums.scala +++ b/enumeratum-play/src/main/scala/enumeratum/values/PlayValueEnums.scala @@ -76,4 +76,40 @@ trait StringPlayEnum[EnumEntry <: StringEnumEntry] extends StringEnum[EnumEntry] with StringPlayPathBindableValueEnum[EnumEntry] with StringPlayQueryBindableValueEnum[EnumEntry] with StringPlayFormValueEnum[EnumEntry] - with StringPlayJsonValueEnum[EnumEntry] \ No newline at end of file + with StringPlayJsonValueEnum[EnumEntry] + +/** + * A ByteEnum that has a lot of the Play-related implicits built-in so you can avoid + * boilerplate. + * + * Things included are: + * + * - implicit PathBindable (for binding from request path) + * - implicit QueryByteBindable (for binding from query strings) + * - formField for doing things like `Form("hello" -> MyEnum.formField)` + * - implicit Json format + * + */ +trait BytePlayEnum[EnumEntry <: ByteEnumEntry] extends ByteEnum[EnumEntry] + with BytePlayPathBindableValueEnum[EnumEntry] + with BytePlayQueryBindableValueEnum[EnumEntry] + with BytePlayFormValueEnum[EnumEntry] + with BytePlayJsonValueEnum[EnumEntry] + +/** + * A CharEnum that has a lot of the Play-related implicits built-in so you can avoid + * boilerplate. + * + * Things included are: + * + * - implicit PathBindable (for binding from request path) + * - implicit QueryCharBindable (for binding from query strings) + * - formField for doing things like `Form("hello" -> MyEnum.formField)` + * - implicit Json format + * + */ +trait CharPlayEnum[EnumEntry <: CharEnumEntry] extends CharEnum[EnumEntry] + with CharPlayPathBindableValueEnum[EnumEntry] + with CharPlayQueryBindableValueEnum[EnumEntry] + with CharPlayFormValueEnum[EnumEntry] + with CharPlayJsonValueEnum[EnumEntry] \ No newline at end of file diff --git a/enumeratum-play/src/test/scala/enumeratum/values/PlayValueEnumSpec.scala b/enumeratum-play/src/test/scala/enumeratum/values/PlayValueEnumSpec.scala index 5485e99d..8c97e6bd 100644 --- a/enumeratum-play/src/test/scala/enumeratum/values/PlayValueEnumSpec.scala +++ b/enumeratum-play/src/test/scala/enumeratum/values/PlayValueEnumSpec.scala @@ -1,7 +1,8 @@ package enumeratum.values import org.scalatest.{ FunSpec, Matchers } -import play.api.libs.json.JsString +import play.api.libs.json.{ JsNumber, JsString } +import EnumFormats.charFormat /** * Created by Lloyd on 4/13/16. @@ -14,6 +15,8 @@ class PlayValueEnumSpec extends FunSpec with Matchers with PlayValueEnumHelpers testNumericPlayEnum("ShortPlayEnum", PlayDrinks) testNumericPlayEnum("IntPlayEnum", PlayLibraryItem) testPlayEnum("StringPlayEnum", PlayOperatingSystem, JsString) + testPlayEnum("BytePlayEnum", PlayBites, { s: Byte => JsNumber(s) }) + testPlayEnum("CharPlayEnum", PlayAlphabet, { s: Char => JsString(s"$s") }) testNumericPlayEnum("IntPlayEnum with values declared as members", PlayMovieGenre) } @@ -89,3 +92,27 @@ case object PlayMovieGenre extends IntPlayEnum[PlayMovieGenre] { val values = findValues } + +sealed abstract class PlayAlphabet(val value: Char) extends CharEnumEntry + +case object PlayAlphabet extends CharEnum[PlayAlphabet] with CharPlayEnum[PlayAlphabet] { + + case object A extends PlayAlphabet('A') + case object B extends PlayAlphabet('B') + case object C extends PlayAlphabet('C') + case object D extends PlayAlphabet('D') + + val values = findValues + +} + +sealed abstract class PlayBites(val value: Byte) extends ByteEnumEntry + +object PlayBites extends ByteEnum[PlayBites] with BytePlayEnum[PlayBites] { + val values = findValues + + case object OneByte extends PlayBites(1) + case object TwoByte extends PlayBites(2) + case object ThreeByte extends PlayBites(3) + case object FourByte extends PlayBites(4) +} diff --git a/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/BSONValueHandlers.scala b/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/BSONValueHandlers.scala index 5579c77c..a7785f24 100644 --- a/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/BSONValueHandlers.scala +++ b/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/BSONValueHandlers.scala @@ -51,6 +51,20 @@ trait BSONValueReads { } } + implicit val bsonReaderChar = new BSONReader[BSONValue, Char] { + def read(bson: BSONValue): Char = bson match { + case BSONString(x) if x.length == 1 => x.charAt(0) + case _ => throw new RuntimeException(s"Could not convert $bson to Char") + } + } + + implicit val bsonReaderByte = new BSONReader[BSONValue, Byte] { + def read(bson: BSONValue): Byte = bson match { + case BSONInteger(x) => x.toByte + case _ => throw new RuntimeException(s"Could not convert $bson to Byte") + } + } + } trait BSONValueWrites { @@ -71,4 +85,12 @@ trait BSONValueWrites { def write(t: String): BSONValue = BSONString(t) } + implicit val bsonWriterChar = new BSONWriter[Char, BSONValue] { + def write(t: Char): BSONValue = BSONString(s"$t") + } + + implicit val bsonWriterByte = new BSONWriter[Byte, BSONValue] { + def write(t: Byte): BSONValue = BSONInteger(t) + } + } \ No newline at end of file diff --git a/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/ReactiveMongoBsonValueEnum.scala b/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/ReactiveMongoBsonValueEnum.scala index e907b2d6..1bdb495a 100644 --- a/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/ReactiveMongoBsonValueEnum.scala +++ b/enumeratum-reactivemongo-bson/src/main/scala/enumeratum/values/ReactiveMongoBsonValueEnum.scala @@ -52,3 +52,21 @@ trait StringReactiveMongoBsonValueEnum[EntryType <: StringEnumEntry] extends Rea implicit val bsonHandler: BSONHandler[BSONValue, EntryType] = EnumHandler.handler(this) } + +/** + * Enum implementation for Char enum members that contains an implicit ReactiveMongo BSON Handler + */ +trait CharReactiveMongoBsonValueEnum[EntryType <: CharEnumEntry] extends ReactiveMongoBsonValueEnum[Char, EntryType] { + this: CharEnum[EntryType] => + + implicit val bsonHandler: BSONHandler[BSONValue, EntryType] = EnumHandler.handler(this) +} + +/** + * Enum implementation for Byte enum members that contains an implicit ReactiveMongo BSON Handler + */ +trait ByteReactiveMongoBsonValueEnum[EntryType <: ByteEnumEntry] extends ReactiveMongoBsonValueEnum[Byte, EntryType] { + this: ByteEnum[EntryType] => + + implicit val bsonHandler: BSONHandler[BSONValue, EntryType] = EnumHandler.handler(this) +} diff --git a/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/BsonEnums.scala b/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/BsonEnums.scala index 6ed5bcdf..63ea9c74 100644 --- a/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/BsonEnums.scala +++ b/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/BsonEnums.scala @@ -57,4 +57,28 @@ case object BsonOperatingSystem extends StringEnum[BsonOperatingSystem] with Str val values = findValues -} \ No newline at end of file +} + +sealed abstract class BsonAlphabet(val value: Char) extends CharEnumEntry + +case object BsonAlphabet extends CharEnum[BsonAlphabet] with CharReactiveMongoBsonValueEnum[BsonAlphabet] { + + case object A extends BsonAlphabet('A') + case object B extends BsonAlphabet('B') + case object C extends BsonAlphabet('C') + case object D extends BsonAlphabet('D') + + val values = findValues + +} + +sealed abstract class BsonBites(val value: Byte) extends ByteEnumEntry + +object BsonBites extends ByteEnum[BsonBites] with ByteReactiveMongoBsonValueEnum[BsonBites] { + val values = findValues + + case object OneByte extends BsonBites(1) + case object TwoByte extends BsonBites(2) + case object ThreeByte extends BsonBites(3) + case object FourByte extends BsonBites(4) +} diff --git a/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/EnumBsonHandlerSpec.scala b/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/EnumBsonHandlerSpec.scala index 6ae84dfc..12ea7571 100644 --- a/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/EnumBsonHandlerSpec.scala +++ b/enumeratum-reactivemongo-bson/src/test/scala/enumeratum/values/EnumBsonHandlerSpec.scala @@ -15,6 +15,8 @@ class EnumBsonHandlerSpec extends FunSpec with Matchers with EnumBsonHandlerHelp testReader("LongEnum", ContentType) testReader("ShortEnum", Drinks) testReader("StringEnum", OperatingSystem) + testReader("ByteEnum", Bites) + testReader("CharEnum", Alphabet) } @@ -24,6 +26,8 @@ class EnumBsonHandlerSpec extends FunSpec with Matchers with EnumBsonHandlerHelp testWriter("LongEnum", ContentType) testWriter("ShortEnum", Drinks) testWriter("StringEnum", OperatingSystem) + testWriter("ByteEnum", Bites) + testWriter("CharEnum", Alphabet) } @@ -37,6 +41,8 @@ class EnumBsonHandlerSpec extends FunSpec with Matchers with EnumBsonHandlerHelp testHandler("LongReactiveMongoBsonValueEnum", BsonContentType, Some(BsonContentType.bsonHandler)) testHandler("IntReactiveMongoBsonValueEnum", BsonLibraryItem, Some(BsonLibraryItem.bsonHandler)) testHandler("StringReactiveMongoBsonValueEnum", BsonOperatingSystem, Some(BsonOperatingSystem.bsonHandler)) + testHandler("CharReactiveMongoBsonValueEnum", BsonAlphabet, Some(BsonAlphabet.bsonHandler)) + testHandler("ByteReactiveMongoBsonValueEnum", BsonBites, Some(BsonBites.bsonHandler)) } diff --git a/enumeratum-upickle/src/main/scala/enumeratum/values/UPickleValueEnum.scala b/enumeratum-upickle/src/main/scala/enumeratum/values/UPickleValueEnum.scala index cbf2ec87..3c409090 100644 --- a/enumeratum-upickle/src/main/scala/enumeratum/values/UPickleValueEnum.scala +++ b/enumeratum-upickle/src/main/scala/enumeratum/values/UPickleValueEnum.scala @@ -46,3 +46,17 @@ trait ShortUPickleEnum[EntryType <: ShortEnumEntry] extends UPickleValueEnum[Sho trait StringUPickleEnum[EntryType <: StringEnumEntry] extends UPickleValueEnum[String, EntryType] { this: ValueEnum[String, EntryType] => implicit val uPickleReadWriter: RW[EntryType] = ReadWriter(writer(this).write, reader(this).read) } + +/** + * Enum implementation for Byte enum members that contains an implicit UPickle ReadWriter + */ +trait ByteUPickleEnum[EntryType <: ByteEnumEntry] extends UPickleValueEnum[Byte, EntryType] { this: ValueEnum[Byte, EntryType] => + implicit val uPickleReadWriter: RW[EntryType] = ReadWriter(writer(this).write, reader(this).read) +} + +/** + * Enum implementation for Char enum members that contains an implicit UPickle ReadWriter + */ +trait CharUPickleEnum[EntryType <: CharEnumEntry] extends UPickleValueEnum[Char, EntryType] { this: ValueEnum[Char, EntryType] => + implicit val uPickleReadWriter: RW[EntryType] = ReadWriter(writer(this).write, reader(this).read) +} diff --git a/enumeratum-upickle/src/test/scala/enumeratum/values/UPicklerSpec.scala b/enumeratum-upickle/src/test/scala/enumeratum/values/UPicklerSpec.scala index 8f43d49e..df90455d 100644 --- a/enumeratum-upickle/src/test/scala/enumeratum/values/UPicklerSpec.scala +++ b/enumeratum-upickle/src/test/scala/enumeratum/values/UPicklerSpec.scala @@ -15,6 +15,8 @@ class UPicklerSpec extends FunSpec with Matchers { testPickling("ShortUPickleEnum", UPickleDrinks) testPickling("IntUPickleEnum", UPickleLibraryItem) testPickling("StringUPickleEnum", UPickleOperatingSystem) + testPickling("ByteUPickleEnum", UPickleBites) + testPickling("CharUPickleEnum", UPickleAlphabet) testPickling("IntUPickleEnum with values declared as members", UPickleMovieGenre) /** @@ -33,7 +35,7 @@ class UPicklerSpec extends FunSpec with Matchers { it("should fail with invalid values") { intercept[Exception] { - readJs(Js.Str("D")) + readJs(Js.Str("ababab")) } intercept[Exception] { readJs(Js.Num(Int.MaxValue)) @@ -129,3 +131,27 @@ case object UPickleMovieGenre extends IntEnum[UPickleMovieGenre] with IntUPickle val values = findValues } + +sealed abstract class UPickleAlphabet(val value: Char) extends CharEnumEntry + +case object UPickleAlphabet extends CharEnum[UPickleAlphabet] with CharUPickleEnum[UPickleAlphabet] { + + case object A extends UPickleAlphabet('A') + case object B extends UPickleAlphabet('B') + case object C extends UPickleAlphabet('C') + case object D extends UPickleAlphabet('D') + + val values = findValues + +} + +sealed abstract class UPickleBites(val value: Byte) extends ByteEnumEntry + +object UPickleBites extends ByteEnum[UPickleBites] with ByteUPickleEnum[UPickleBites] { + val values = findValues + + case object OneByte extends UPickleBites(1) + case object TwoByte extends UPickleBites(2) + case object ThreeByte extends UPickleBites(3) + case object FourByte extends UPickleBites(4) +} diff --git a/macros/compat/src/main/scala-2.10/enumeratum/ContextUtils.scala b/macros/compat/src/main/scala-2.10/enumeratum/ContextUtils.scala index 794cb8eb..bfcee0d1 100644 --- a/macros/compat/src/main/scala-2.10/enumeratum/ContextUtils.scala +++ b/macros/compat/src/main/scala-2.10/enumeratum/ContextUtils.scala @@ -7,6 +7,7 @@ object ContextUtils { // In 2.10, the constants have Java boxed types at compile time for some reason type CTLong = java.lang.Long type CTInt = java.lang.Integer + type CTChar = java.lang.Character /** * Returns a TermName diff --git a/macros/compat/src/main/scala-2.11/enumeratum/ContextUtils.scala b/macros/compat/src/main/scala-2.11/enumeratum/ContextUtils.scala index 50d406e2..fa149bd4 100644 --- a/macros/compat/src/main/scala-2.11/enumeratum/ContextUtils.scala +++ b/macros/compat/src/main/scala-2.11/enumeratum/ContextUtils.scala @@ -7,6 +7,7 @@ object ContextUtils { // Constant types type CTLong = Long type CTInt = Int + type CTChar = Char /** * Returns a TermName diff --git a/macros/src/main/scala/enumeratum/ValueEnumMacros.scala b/macros/src/main/scala/enumeratum/ValueEnumMacros.scala index 9d4bc44e..9ba498cb 100644 --- a/macros/src/main/scala/enumeratum/ValueEnumMacros.scala +++ b/macros/src/main/scala/enumeratum/ValueEnumMacros.scala @@ -47,6 +47,28 @@ object ValueEnumMacros { findValueEntriesImpl[ValueEntryType, String, String](c)(identity) } + /** + * Finds ValueEntryType-typed objects in scope that have literal value:Byte implementations + * + * Note + * + * - requires the ValueEntryType to have a 'value' member that has a literal value + */ + def findByteValueEntriesImpl[ValueEntryType: c.WeakTypeTag](c: Context): c.Expr[IndexedSeq[ValueEntryType]] = { + findValueEntriesImpl[ValueEntryType, ContextUtils.CTInt, Byte](c)(_.toByte) + } + + /** + * Finds ValueEntryType-typed objects in scope that have literal value:Char implementations + * + * Note + * + * - requires the ValueEntryType to have a 'value' member that has a literal value + */ + def findCharValueEntriesImpl[ValueEntryType: c.WeakTypeTag](c: Context): c.Expr[IndexedSeq[ValueEntryType]] = { + findValueEntriesImpl[ValueEntryType, ContextUtils.CTChar, Char](c)(identity) + } + /** * The method that does the heavy lifting. */