Skip to content

Commit 3d89d75

Browse files
Julian KozłowskiJulian Kozłowski
authored andcommitted
Adding enum support
1 parent c577fb8 commit 3d89d75

5 files changed

Lines changed: 169 additions & 80 deletions

File tree

Lines changed: 31 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,27 @@
1-
/* package pl.iterators.kebs.jsoniter.enums
1+
package pl.iterators.kebs.jsoniter.enums
22

33
import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry}
44
import com.github.plokhotnyuk.jsoniter_scala.core._
5-
import com.github.plokhotnyuk.jsoniter_scala.macros._
65

76
trait KebsJsoniterEnums {
8-
@inline protected final def enumNameDeserializationError[E](`enum`: EnumLike[E], name: String): String = {
9-
val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ")
10-
s"$name should be one of $enumNames"
11-
}
12-
13-
@inline protected final def enumValueDeserializationError[E](`enum`: EnumLike[E], value: String): String = {
14-
val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ")
15-
s"$value should be a string of value $enumNames"
16-
}
17-
18-
protected final def enumJsonValueCodec[E](`enum`: EnumLike[E], map: E => String, comap: String => Option[E]): JsonValueCodec[E] = {
7+
protected final def enumJsonValueCodec[E](
8+
`enum`: EnumLike[E],
9+
map: E => String,
10+
comap: String => Option[E]
11+
): JsonValueCodec[E] =
1912
new JsonValueCodec[E] {
2013
override def decodeValue(in: JsonReader, default: E): E = {
2114
val stringValue = in.readString(null)
2215
comap(stringValue).getOrElse {
23-
val enumValues = `enum`.valuesToNamesMap.values.mkString(", ")
24-
throw new JsoniterReaderExceptionImpl(s"$stringValue is not a member of enum values: $enumValues")
16+
val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ")
17+
in.decodeError(s"$stringValue should be one of $enumNames")
2518
}
2619
}
27-
28-
override def encodeValue(x: E, out: JsonWriter): Unit = {
29-
val stringValue = map(x)
30-
out.writeVal(stringValue)
31-
}
32-
33-
override def nullValue: E = null.asInstanceOf[E]
20+
override def encodeValue(x: E, out: JsonWriter): Unit = out.writeVal(map(x))
21+
override def nullValue: E = null.asInstanceOf[E]
3422
}
35-
}
3623

37-
def enumJsonValueCodec[E](`enum`: EnumLike[E]): JsonValueCodec[E] =
24+
def defaultEnumJsonValueCodec[E](`enum`: EnumLike[E]): JsonValueCodec[E] =
3825
enumJsonValueCodec[E](`enum`, _.toString, `enum`.withNameInsensitiveOption(_))
3926

4027
def uppercaseEnumJsonValueCodec[E](`enum`: EnumLike[E]): JsonValueCodec[E] =
@@ -43,67 +30,35 @@ trait KebsJsoniterEnums {
4330
def lowercaseEnumJsonValueCodec[E](`enum`: EnumLike[E]): JsonValueCodec[E] =
4431
enumJsonValueCodec[E](`enum`, _.toString.toLowerCase, `enum`.withNameLowercaseOnlyOption(_))
4532

46-
implicit def enumCodecImpl[E](implicit ev: EnumLike[E]): JsonValueCodec[E] = enumJsonValueCodec(ev)
33+
implicit def enumCodec[E](implicit ev: EnumLike[E]): JsonValueCodec[E] = defaultEnumJsonValueCodec(ev)
34+
}
4735

48-
trait KebsJsoniterEnumsUppercase {
49-
implicit def enumJsonValueCodecImpl[E](implicit ev: EnumLike[E]): JsonValueCodec[E] =
50-
uppercaseEnumJsonValueCodec(ev)
51-
}
36+
trait KebsJsoniterEnumsUppercase extends KebsJsoniterEnums {
37+
override implicit def enumCodec[E](implicit ev: EnumLike[E]): JsonValueCodec[E] = uppercaseEnumJsonValueCodec(ev)
38+
}
5239

53-
trait KebsJsoniterEnumsLowercase {
54-
implicit def enumJsonValueCodecImpl[E](implicit ev: EnumLike[E]): JsonValueCodec[E] =
55-
lowercaseEnumJsonValueCodec(ev)
56-
}
40+
trait KebsJsoniterEnumsLowercase extends KebsJsoniterEnums {
41+
override implicit def enumCodec[E](implicit ev: EnumLike[E]): JsonValueCodec[E] = lowercaseEnumJsonValueCodec(ev)
5742
}
5843

5944
trait KebsJsoniterValueEnums {
60-
sealed trait NotGiven[A]
61-
62-
object NotGiven extends LowPriorityNotGiven {
63-
implicit def amb1[A](implicit ev: A): NotGiven[A] = sys.error("should not be called")
64-
implicit def amb2[A](implicit ev: A): NotGiven[A] = sys.error("should not be called")
65-
}
66-
67-
trait LowPriorityNotGiven {
68-
implicit def notFound[A]: NotGiven[A] = new NotGiven[A] {}
69-
}
70-
71-
inline implicit def exportCodec[E]: JsonValueCodec[E] = {
72-
JsonCodecMaker.make[E](
73-
CodecMakerConfig
74-
.withDiscriminatorFieldName(None)
75-
.withAllowRecursiveTypes(true)
76-
.withTransientEmpty(false)
77-
.withTransientNull(false)
78-
.withTransientNone(false)
79-
)
80-
}
81-
82-
@inline protected final def valueEnumDeserializationError[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E], value: V) = {
83-
val enumValues = `enum`.getValuesToEntriesMap.keys.mkString(", ")
84-
throw new JsoniterReaderExceptionImpl(s"$value is not a member of $enumValues")
85-
}
86-
87-
def valueEnumCodec[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit baseJsonCodec: JsonValueCodec[V]) =
45+
def valueEnumJsonValueCodec[V, E <: ValueEnumLikeEntry[V]](
46+
`enum`: ValueEnumLike[V, E]
47+
)(implicit baseCodec: JsonValueCodec[V]): JsonValueCodec[E] =
8848
new JsonValueCodec[E] {
8949
override def decodeValue(in: JsonReader, default: E): E = {
90-
val value = baseJsonCodec.decodeValue(in, baseJsonCodec.nullValue)
91-
`enum`.withValueOption(value).getOrElse(valueEnumDeserializationError(`enum`, value))
92-
}
93-
override def encodeValue(x: E, out: JsonWriter): Unit = {
94-
baseJsonCodec.encodeValue(x.value, out)
50+
val value = baseCodec.decodeValue(in, baseCodec.nullValue)
51+
`enum`.withValueOption(value).getOrElse {
52+
val enumValues = `enum`.getValuesToEntriesMap.keys.mkString(", ")
53+
throw new RuntimeException(s"$value is not a member of $enumValues")
54+
}
9555
}
96-
override def nullValue: E = null.asInstanceOf[E]
56+
override def encodeValue(x: E, out: JsonWriter): Unit = baseCodec.encodeValue(x.value, out)
57+
override def nullValue: E = null.asInstanceOf[E]
9758
}
9859

99-
implicit def valueEnumCodecImpl[V, E <: ValueEnumLikeEntry[V]](implicit
60+
implicit def valueEnumCodec[V, E <: ValueEnumLikeEntry[V]](implicit
10061
ev: ValueEnumLike[V, E],
101-
codec: JsonValueCodec[V]
102-
): JsonValueCodec[E] =
103-
valueEnumCodec(ev)
104-
}
105-
106-
class JsoniterReaderExceptionImpl(reason: String) extends Exception {
107-
override def getMessage(): String = reason
62+
baseCodec: JsonValueCodec[V]
63+
): JsonValueCodec[E] = valueEnumJsonValueCodec(ev)
10864
}
109-
*/
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package pl.iterators.kebs.jsoniter
22

3-
// package object enums extends KebsJsoniterEnums with KebsJsoniterValueEnums {
4-
// object uppercase extends KebsCirceEnumsUppercase with KebsCirceValueEnums
5-
// object lowercase extends KebsCirceEnumsLowercase with KebsCirceValueEnums
6-
// }
3+
package object enums extends KebsJsoniterEnums with KebsJsoniterValueEnums {
4+
object uppercase extends KebsJsoniterEnumsUppercase with KebsJsoniterValueEnums
5+
object lowercase extends KebsJsoniterEnumsLowercase with KebsJsoniterValueEnums
6+
}

jsoniter/src/test/scala-2/pl/iterators/kebs/jsoniter/model/model.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
package pl.iterators.kebs.jsoniter
22

33
import java.time.ZonedDateTime
4+
import enumeratum.values.{LongEnum, LongEnumEntry}
5+
import enumeratum.{Enum, EnumEntry}
46
import pl.iterators.kebs.core.enums.ValueEnumLikeEntry
57
import com.github.plokhotnyuk.jsoniter_scala.core._
68

79
package object model {
810

11+
sealed abstract class LongGreeting(val value: Long) extends LongEnumEntry with ValueEnumLikeEntry[Long]
12+
13+
object LongGreeting extends LongEnum[LongGreeting] {
14+
val values = findValues
15+
16+
case object Hello extends LongGreeting(0L)
17+
case object GoodBye extends LongGreeting(1L)
18+
case object Hi extends LongGreeting(2L)
19+
case object Bye extends LongGreeting(3L)
20+
}
21+
22+
sealed trait Greeting extends EnumEntry
23+
24+
object Greeting extends Enum[Greeting] {
25+
val values = findValues
26+
27+
case object Hello extends Greeting
28+
case object GoodBye extends Greeting
29+
case object Hi extends Greeting
30+
case object Bye extends Greeting
31+
}
32+
933
case class C(anInteger: Int)
1034
case class D(intField: Int, stringField: String)
1135
case class E(noFormat: ZonedDateTime)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package pl.iterators.kebs.jsoniter.formats
2+
3+
import com.github.plokhotnyuk.jsoniter_scala.core._
4+
import org.scalatest.funsuite.AnyFunSuite
5+
import org.scalatest.matchers.should.Matchers
6+
import pl.iterators.kebs.jsoniter.KebsEnumForTests
7+
import pl.iterators.kebs.jsoniter.enums.{KebsJsoniterEnums, KebsJsoniterEnumsUppercase, KebsJsoniterEnumsLowercase}
8+
import pl.iterators.kebs.jsoniter.model.Greeting
9+
import pl.iterators.kebs.jsoniter.model.Greeting._
10+
11+
class JsoniterEnumFormatTests extends AnyFunSuite with Matchers with KebsEnumForTests {
12+
13+
object KebsProtocol extends KebsJsoniterEnums
14+
object KebsProtocolUppercase extends KebsJsoniterEnumsUppercase
15+
object KebsProtocolLowercase extends KebsJsoniterEnumsLowercase
16+
17+
test("enum codec - case insensitive read") {
18+
import KebsProtocol._
19+
val codec = implicitly[JsonValueCodec[Greeting]]
20+
readFromString[Greeting]("\"hElLo\"")(codec) shouldBe Hello
21+
readFromString[Greeting]("\"goodbye\"")(codec) shouldBe GoodBye
22+
}
23+
24+
test("enum codec - write") {
25+
import KebsProtocol._
26+
val codec = implicitly[JsonValueCodec[Greeting]]
27+
writeToString[Greeting](Hello)(codec) shouldBe "\"Hello\""
28+
writeToString[Greeting](GoodBye)(codec) shouldBe "\"GoodBye\""
29+
}
30+
31+
test("enum codec - deserialization error on wrong value") {
32+
import KebsProtocol._
33+
val codec = implicitly[JsonValueCodec[Greeting]]
34+
intercept[JsonReaderException] {
35+
readFromString[Greeting]("\"NotAGreeting\"")(codec)
36+
}.getMessage should include("NotAGreeting should be one of")
37+
}
38+
39+
test("enum codec - deserialization error on non-string") {
40+
import KebsProtocol._
41+
val codec = implicitly[JsonValueCodec[Greeting]]
42+
intercept[JsonReaderException] {
43+
readFromString[Greeting]("1")(codec)
44+
}
45+
}
46+
47+
test("enum codec - uppercase read") {
48+
import KebsProtocolUppercase._
49+
val codec = implicitly[JsonValueCodec[Greeting]]
50+
readFromString[Greeting]("\"HELLO\"")(codec) shouldBe Hello
51+
readFromString[Greeting]("\"GOODBYE\"")(codec) shouldBe GoodBye
52+
}
53+
54+
test("enum codec - uppercase write") {
55+
import KebsProtocolUppercase._
56+
val codec = implicitly[JsonValueCodec[Greeting]]
57+
writeToString[Greeting](Hello)(codec) shouldBe "\"HELLO\""
58+
writeToString[Greeting](GoodBye)(codec) shouldBe "\"GOODBYE\""
59+
}
60+
61+
test("enum codec - lowercase read") {
62+
import KebsProtocolLowercase._
63+
val codec = implicitly[JsonValueCodec[Greeting]]
64+
readFromString[Greeting]("\"hello\"")(codec) shouldBe Hello
65+
readFromString[Greeting]("\"goodbye\"")(codec) shouldBe GoodBye
66+
}
67+
68+
test("enum codec - lowercase write") {
69+
import KebsProtocolLowercase._
70+
val codec = implicitly[JsonValueCodec[Greeting]]
71+
writeToString[Greeting](Hello)(codec) shouldBe "\"hello\""
72+
writeToString[Greeting](GoodBye)(codec) shouldBe "\"goodbye\""
73+
}
74+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package pl.iterators.kebs.jsoniter.formats
2+
3+
import com.github.plokhotnyuk.jsoniter_scala.core._
4+
import org.scalatest.funsuite.AnyFunSuite
5+
import org.scalatest.matchers.should.Matchers
6+
import pl.iterators.kebs.jsoniter.{KebsJsoniter, KebsValueEnumForTests}
7+
import pl.iterators.kebs.jsoniter.enums.KebsJsoniterValueEnums
8+
import pl.iterators.kebs.jsoniter.model.LongGreeting
9+
import pl.iterators.kebs.jsoniter.model.LongGreeting._
10+
11+
class JsoniterValueEnumFormatTests extends AnyFunSuite with Matchers with KebsValueEnumForTests {
12+
13+
object KebsProtocol extends KebsJsoniter with KebsJsoniterValueEnums
14+
15+
test("value enum codec - read") {
16+
import KebsProtocol._
17+
val codec = implicitly[JsonValueCodec[LongGreeting]]
18+
readFromString[LongGreeting]("0")(codec) shouldBe Hello
19+
readFromString[LongGreeting]("1")(codec) shouldBe GoodBye
20+
}
21+
22+
test("value enum codec - write") {
23+
import KebsProtocol._
24+
val codec = implicitly[JsonValueCodec[LongGreeting]]
25+
writeToString[LongGreeting](Hello)(codec) shouldBe "0"
26+
writeToString[LongGreeting](GoodBye)(codec) shouldBe "1"
27+
}
28+
29+
test("value enum codec - deserialization error") {
30+
import KebsProtocol._
31+
val codec = implicitly[JsonValueCodec[LongGreeting]]
32+
intercept[RuntimeException] {
33+
readFromString[LongGreeting]("4")(codec)
34+
}.getMessage should include("4 is not a member of")
35+
}
36+
}

0 commit comments

Comments
 (0)