-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from ChrisMcD1/enumeratum-value-enums
Add Enumeratum ValueEnum codecs
- Loading branch information
Showing
3 changed files
with
263 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
modules/enumeratum/src/main/scala/phobos/enumeratum/XmlValueEnum.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package phobos.enumeratum | ||
|
||
import phobos.decoding._ | ||
import phobos.encoding._ | ||
|
||
import enumeratum.values._ | ||
|
||
sealed trait XmlValueEnum[V, E <: ValueEnumEntry[V]] { | ||
_enum: ValueEnum[V, E] => | ||
|
||
implicit def elementEncoder: ElementEncoder[E] | ||
implicit def attributeEncoder: AttributeEncoder[E] | ||
implicit def textEncoder: TextEncoder[E] | ||
implicit def elementDecoder: ElementDecoder[E] | ||
implicit def attributeDecoder: AttributeDecoder[E] | ||
implicit def textDecoder: TextDecoder[E] | ||
|
||
} | ||
|
||
trait IntXmlValueEnum[E <: IntEnumEntry] extends XmlValueEnum[Int, E] { | ||
self: IntEnum[E] => | ||
implicit val elementEncoder: ElementEncoder[E] = XmlValueEnum.elementEncoder[Int, E] | ||
implicit val attributeEncoder: AttributeEncoder[E] = XmlValueEnum.attributeEncoder[Int, E] | ||
implicit val textEncoder: TextEncoder[E] = XmlValueEnum.textEncoder[Int, E] | ||
implicit val elementDecoder: ElementDecoder[E] = XmlValueEnum.elementDecoder(self) | ||
implicit val attributeDecoder: AttributeDecoder[E] = XmlValueEnum.attributeDecoder(self) | ||
implicit val textDecoder: TextDecoder[E] = XmlValueEnum.textDecoder(self) | ||
} | ||
|
||
trait LongXmlValueEnum[E <: LongEnumEntry] extends XmlValueEnum[Long, E] { | ||
self: LongEnum[E] => | ||
implicit val elementEncoder: ElementEncoder[E] = XmlValueEnum.elementEncoder[Long, E] | ||
implicit val attributeEncoder: AttributeEncoder[E] = XmlValueEnum.attributeEncoder[Long, E] | ||
implicit val textEncoder: TextEncoder[E] = XmlValueEnum.textEncoder[Long, E] | ||
implicit val elementDecoder: ElementDecoder[E] = XmlValueEnum.elementDecoder(self) | ||
implicit val attributeDecoder: AttributeDecoder[E] = XmlValueEnum.attributeDecoder(self) | ||
implicit val textDecoder: TextDecoder[E] = XmlValueEnum.textDecoder(self) | ||
} | ||
|
||
trait ShortXmlValueEnum[E <: ShortEnumEntry] extends XmlValueEnum[Short, E] { | ||
self: ShortEnum[E] => | ||
implicit val elementEncoder: ElementEncoder[E] = XmlValueEnum.elementEncoder[Short, E] | ||
implicit val attributeEncoder: AttributeEncoder[E] = XmlValueEnum.attributeEncoder[Short, E] | ||
implicit val textEncoder: TextEncoder[E] = XmlValueEnum.textEncoder[Short, E] | ||
implicit val elementDecoder: ElementDecoder[E] = XmlValueEnum.elementDecoder(self) | ||
implicit val attributeDecoder: AttributeDecoder[E] = XmlValueEnum.attributeDecoder(self) | ||
implicit val textDecoder: TextDecoder[E] = XmlValueEnum.textDecoder(self) | ||
} | ||
|
||
trait StringXmlValueEnum[E <: StringEnumEntry] extends XmlValueEnum[String, E] { | ||
self: StringEnum[E] => | ||
implicit val elementEncoder: ElementEncoder[E] = XmlValueEnum.elementEncoder[String, E] | ||
implicit val attributeEncoder: AttributeEncoder[E] = XmlValueEnum.attributeEncoder[String, E] | ||
implicit val textEncoder: TextEncoder[E] = XmlValueEnum.textEncoder[String, E] | ||
implicit val elementDecoder: ElementDecoder[E] = XmlValueEnum.elementDecoder(self) | ||
implicit val attributeDecoder: AttributeDecoder[E] = XmlValueEnum.attributeDecoder(self) | ||
implicit val textDecoder: TextDecoder[E] = XmlValueEnum.textDecoder(self) | ||
} | ||
|
||
trait CharXmlValueEnum[E <: CharEnumEntry] extends XmlValueEnum[Char, E] { | ||
self: CharEnum[E] => | ||
implicit val elementEncoder: ElementEncoder[E] = XmlValueEnum.elementEncoder[Char, E] | ||
implicit val attributeEncoder: AttributeEncoder[E] = XmlValueEnum.attributeEncoder[Char, E] | ||
implicit val textEncoder: TextEncoder[E] = XmlValueEnum.textEncoder[Char, E] | ||
implicit val elementDecoder: ElementDecoder[E] = XmlValueEnum.elementDecoder(self) | ||
implicit val attributeDecoder: AttributeDecoder[E] = XmlValueEnum.attributeDecoder(self) | ||
implicit val textDecoder: TextDecoder[E] = XmlValueEnum.textDecoder(self) | ||
} | ||
|
||
trait ByteXmlValueEnum[E <: ByteEnumEntry] extends XmlValueEnum[Byte, E] { | ||
self: ByteEnum[E] => | ||
implicit val elementEncoder: ElementEncoder[E] = XmlValueEnum.elementEncoder[Byte, E] | ||
implicit val attributeEncoder: AttributeEncoder[E] = XmlValueEnum.attributeEncoder[Byte, E] | ||
implicit val textEncoder: TextEncoder[E] = XmlValueEnum.textEncoder[Byte, E] | ||
implicit val elementDecoder: ElementDecoder[E] = XmlValueEnum.elementDecoder(self) | ||
implicit val attributeDecoder: AttributeDecoder[E] = XmlValueEnum.attributeDecoder(self) | ||
implicit val textDecoder: TextDecoder[E] = XmlValueEnum.textDecoder(self) | ||
} | ||
|
||
object XmlValueEnum { | ||
|
||
def elementDecoder[V, E <: ValueEnumEntry[V]]( | ||
e: ValueEnum[V, E], | ||
)(implicit baseDecoder: ElementDecoder[V]): ElementDecoder[E] = | ||
baseDecoder.emap(decodeFromValueType(e)) | ||
|
||
def attributeDecoder[V, E <: ValueEnumEntry[V]]( | ||
e: ValueEnum[V, E], | ||
)(implicit baseDecoder: AttributeDecoder[V]): AttributeDecoder[E] = | ||
baseDecoder.emap(decodeFromValueType(e)) | ||
|
||
def textDecoder[V, E <: ValueEnumEntry[V]]( | ||
e: ValueEnum[V, E], | ||
)(implicit baseDecoder: TextDecoder[V]): TextDecoder[E] = | ||
baseDecoder.emap(decodeFromValueType(e)) | ||
|
||
def decodeFromValueType[V, E <: ValueEnumEntry[V]]( | ||
e: ValueEnum[V, E], | ||
)(history: List[String], value: V): Either[DecodingError, E] = | ||
e.withValueOpt(value) match { | ||
case Some(member) => Right(member) | ||
case _ => Left(DecodingError(s"'$value' in not a member of enum $this", history, None)) | ||
} | ||
|
||
def elementEncoder[V, E <: ValueEnumEntry[V]]( | ||
implicit baseEncoder: ElementEncoder[V], | ||
): ElementEncoder[E] = baseEncoder.contramap(_.value) | ||
|
||
def attributeEncoder[V, E <: ValueEnumEntry[V]]( | ||
implicit baseEncoder: AttributeEncoder[V], | ||
): AttributeEncoder[E] = baseEncoder.contramap(_.value) | ||
|
||
def textEncoder[V, E <: ValueEnumEntry[V]]( | ||
implicit baseEncoder: TextEncoder[V], | ||
): TextEncoder[E] = baseEncoder.contramap(_.value) | ||
|
||
} |
141 changes: 141 additions & 0 deletions
141
modules/enumeratum/src/test/scala/phobos/enumeratum/EnumeratumValueTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package phobos.enumeratum | ||
|
||
import phobos.decoding.XmlDecoder | ||
import phobos.derivation.semiauto._ | ||
import phobos.encoding.XmlEncoder | ||
import phobos.syntax._ | ||
import phobos.testString._ | ||
|
||
import enumeratum.values._ | ||
import org.scalatest.Assertion | ||
import org.scalatest.matchers.should.Matchers | ||
import org.scalatest.wordspec.AnyWordSpec | ||
|
||
sealed abstract class Foo(override val value: Int) extends IntEnumEntry with Product with Serializable | ||
object Foo extends IntEnum[Foo] with IntXmlValueEnum[Foo] { | ||
val values = findValues | ||
|
||
case object Foo1 extends Foo(1) | ||
case object Foo2 extends Foo(2) | ||
case object Foo3 extends Foo(3) | ||
} | ||
import Foo._ | ||
case class Bar(d: String, foo: Foo, e: Char) | ||
object Bar { | ||
implicit lazy val xmlEncoder: XmlEncoder[Bar] = deriveXmlEncoder[Bar]("bar") | ||
implicit lazy val xmlDecoder: XmlDecoder[Bar] = deriveXmlDecoder[Bar]("bar") | ||
} | ||
case class Baz(@attr f: Foo, @text text: Foo) | ||
object Baz { | ||
implicit lazy val xmlEncoder: XmlEncoder[Baz] = deriveXmlEncoder[Baz]("baz") | ||
implicit lazy val xmlDecoder: XmlDecoder[Baz] = deriveXmlDecoder[Baz]("baz") | ||
} | ||
|
||
class EnumeratumValueTest extends AnyWordSpec with Matchers { | ||
"Enum codecs" should { | ||
"encode enums" in { | ||
val bar1 = Bar("d value", Foo1, 'e') | ||
val bar2 = Bar("d value", Foo2, 'e') | ||
val bar3 = Bar("another one value", Foo3, 'v') | ||
val baz = Baz(Foo1, Foo2) | ||
val xml1 = XmlEncoder[Bar].encode(bar1) | ||
val xml2 = XmlEncoder[Bar].encode(bar2) | ||
val xml3 = XmlEncoder[Bar].encode(bar3) | ||
val xml4 = XmlEncoder[Baz].encode(baz) | ||
val string1 = | ||
""" | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <bar> | ||
| <d>d value</d> | ||
| <foo> | ||
| 1 | ||
| </foo> | ||
| <e>e</e> | ||
| </bar> | ||
""".stripMargin.minimized | ||
val string2 = | ||
""" | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <bar> | ||
| <d>d value</d> | ||
| <foo> | ||
| 2 | ||
| </foo> | ||
| <e>e</e> | ||
| </bar> | ||
""".stripMargin.minimized | ||
val string3 = | ||
""" | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <bar> | ||
| <d>another one value</d> | ||
| <foo> | ||
| 3 | ||
| </foo> | ||
| <e>v</e> | ||
| </bar> | ||
""".stripMargin.minimized | ||
val string4 = | ||
""" | ||
| <?xml version='1.0' encoding='UTF-8'?> | ||
| <baz f="1">2</baz> | ||
""".stripMargin.minimized | ||
assert( | ||
xml1 == Right(string1) && | ||
xml2 == Right(string2) && | ||
xml3 == Right(string3) && | ||
xml4 == Right(string4), | ||
) | ||
} | ||
|
||
def pure(str: String): List[Array[Byte]] = | ||
List(str.getBytes("UTF-8")) | ||
|
||
def fromIterable(str: String): List[Array[Byte]] = | ||
str.toList.map(c => Array(c.toByte)) | ||
|
||
def decodeEnums(toList: String => List[Array[Byte]]): Assertion = { | ||
val bar1 = Bar("d value", Foo1, 'e') | ||
val bar2 = Bar("d value", Foo2, 'e') | ||
val bar3 = Bar("another one value", Foo3, 'v') | ||
val baz = Baz(Foo1, Foo2) | ||
|
||
val string1 = | ||
"""<?xml version='1.0' encoding='UTF-8'?> | ||
| <bar> | ||
| <d>d value</d> | ||
| <foo>1</foo> | ||
| <e>e</e> | ||
| </bar> | ||
""".stripMargin | ||
val string2 = | ||
"""<?xml version='1.0' encoding='UTF-8'?> | ||
| <bar> | ||
| <d>d value</d> | ||
| <foo>2</foo> | ||
| <e>e</e> | ||
| </bar> | ||
""".stripMargin | ||
val string3 = | ||
"""<?xml version='1.0' encoding='UTF-8'?> | ||
| <bar> | ||
| <d>another one value</d> | ||
| <foo>3</foo> | ||
| <e>v</e> | ||
| </bar> | ||
""".stripMargin | ||
val string4 = | ||
"""<?xml version='1.0' encoding='UTF-8'?> | ||
| <baz f="1">2</baz> | ||
""".stripMargin | ||
val decoded1 = XmlDecoder[Bar].decodeFromIterable(toList(string1)) | ||
val decoded2 = XmlDecoder[Bar].decodeFromIterable(toList(string2)) | ||
val decoded3 = XmlDecoder[Bar].decodeFromIterable(toList(string3)) | ||
val decoded4 = XmlDecoder[Baz].decodeFromIterable(toList(string4)) | ||
assert(decoded1 == Right(bar1) && decoded2 == Right(bar2) && decoded3 == Right(bar3) && decoded4 == Right(baz)) | ||
} | ||
|
||
"decode enums sync" in decodeEnums(pure) | ||
"decode enums async" in decodeEnums(fromIterable) | ||
} | ||
} |