-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add ninny-circe-compat * add a nested field in test
- Loading branch information
1 parent
ef0dc7e
commit ea439a9
Showing
3 changed files
with
196 additions
and
0 deletions.
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
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,91 @@ | ||
package nrktkt.ninny.compat | ||
|
||
import scala.annotation.nowarn | ||
import scala.language.implicitConversions | ||
import scala.util.{Failure, Success} | ||
import io.circe._ | ||
import io.circe.Decoder.Result | ||
import nrktkt.ninny.{FromJson, ToSomeJson, ToSomeJsonValue} | ||
import nrktkt.ninny.ast._ | ||
|
||
trait NinnyToCirce { | ||
import CirceToNinny.asNinny | ||
|
||
implicit def toSomeJsonEncoder[A](implicit | ||
toSomeJson: ToSomeJson[A] | ||
): Encoder[A] = | ||
new Encoder[A] { | ||
override def apply(a: A): Json = toSomeJson.toSome(a) | ||
} | ||
|
||
implicit def fromJsonDecoder[A](implicit | ||
fromJson: FromJson[A] | ||
): Decoder[A] = | ||
new Decoder[A] { | ||
override def apply(c: HCursor): Result[A] = | ||
fromJson.from(c.root.value) match { | ||
case Success(value) => Right(value) | ||
case Failure(_) => | ||
Left(DecodingFailure("decode failure", { List.empty })) | ||
} | ||
} | ||
|
||
implicit def asCirce(json: JsonValue): Json = | ||
json match { | ||
case JsonNull => Json.Null | ||
case JsonBoolean(value) => Json.fromBoolean(value) | ||
case JsonString(value) => Json.fromString(value) | ||
case nrktkt.ninny.ast.JsonDouble(value) => { | ||
if (value % 1 == 0) Json.fromInt(value.toInt) | ||
else Json.fromDoubleOrNull(value) | ||
} | ||
case nrktkt.ninny.ast.JsonDecimal(value) => Json.fromBigDecimal(value) | ||
case JsonArray(value) => Json.fromValues(value.map(asCirce)) | ||
case nrktkt.ninny.ast.JsonObject(value) => | ||
Json.fromJsonObject(io.circe.JsonObject.fromMap { | ||
value.map { case (k, v) => | ||
(k, asCirce(v)) | ||
} | ||
}) | ||
case blob: JsonBlob => Json.fromString(nrktkt.ninny.Json.render(blob)) | ||
} | ||
} | ||
|
||
object NinnyToCirce extends NinnyToCirce | ||
|
||
trait CirceToNinny { | ||
import NinnyToCirce.asCirce | ||
|
||
implicit def encoderToSomeJson[A](implicit | ||
encoder: Encoder[A] | ||
): ToSomeJson[A] = | ||
new ToSomeJsonValue[A, JsonValue] { | ||
override def toSome(a: A): JsonValue = encoder.apply(a) | ||
} | ||
|
||
implicit def decoderFromJson[A](implicit decoder: Decoder[A]): FromJson[A] = | ||
FromJson.fromSome(jsonValue => { decoder.decodeJson(jsonValue).toTry }) | ||
|
||
@nowarn | ||
implicit def asNinny(json: Json): JsonValue = json match { | ||
case _ if json.isNull => JsonNull | ||
case _ if json.isBoolean => JsonBoolean(json.asBoolean.get) | ||
case _ if json.isArray => | ||
JsonArray(json.asArray.get.map(asNinny).toIndexedSeq) | ||
case _ if json.isString => JsonString(json.asString.get) | ||
case _ if json.isNumber => | ||
json.asNumber match { | ||
case Some(jsonNumber) => | ||
jsonNumber.toBigDecimal match { | ||
case Some(bigDecimal) => nrktkt.ninny.ast.JsonDecimal(bigDecimal) | ||
} | ||
nrktkt.ninny.ast.JsonNumber(jsonNumber.toDouble) | ||
} | ||
case _ if json.isObject => | ||
nrktkt.ninny.ast.JsonObject(json.asObject.get.toMap.map { case (k, v) => | ||
(k, asNinny(v)) | ||
}) | ||
} | ||
} | ||
|
||
object CirceToNinny extends CirceToNinny |
88 changes: 88 additions & 0 deletions
88
circe-compat/test/src/nrktkt/ninny/compat/CirceCompatSpec.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,88 @@ | ||
package nrktkt.ninny.compat | ||
|
||
import io.circe._ | ||
import io.circe.generic.semiauto._ | ||
import io.circe.syntax._ | ||
import nrktkt.ninny.ast._ | ||
import nrktkt.ninny._ | ||
import org.scalatest.flatspec.AnyFlatSpec | ||
import org.scalatest.matchers.should | ||
import org.scalatest.OptionValues | ||
import org.scalatest.TryValues | ||
|
||
class CirceCompatSpec | ||
extends AnyFlatSpec | ||
with should.Matchers | ||
with OptionValues | ||
with TryValues { | ||
|
||
case class Person1(name: String, age: Int, married: Boolean) | ||
object Person1 { | ||
implicit val decoder: Decoder[Person1] = deriveDecoder[Person1] | ||
implicit val encoder: Encoder[Person1] = deriveEncoder[Person1] | ||
} | ||
|
||
case class Example1(foo: String, bar: Seq[Int], person: Person1) | ||
object Example1 { | ||
implicit val decoder: Decoder[Example1] = deriveDecoder[Example1] | ||
implicit val encoder: Encoder[Example1] = deriveEncoder[Example1] | ||
} | ||
|
||
case class Person2(name: String, age: Int, married: Boolean) | ||
object Person2 { | ||
implicit val toJson = ToJson.auto[Person2] | ||
implicit val fromJson = FromJson.auto[Person2] | ||
} | ||
|
||
case class Example2(foo: String, bar: Seq[Int], person: Person2) | ||
object Example2 { | ||
implicit val toJson = ToJson.auto[Example2] | ||
implicit val fromJson = FromJson.auto[Example2] | ||
} | ||
|
||
val ex1 = Example1("baz", Seq(1, 2, 3), Person1("Alice", 27, false)) | ||
val ex2 = Example2("baz", Seq(1, 2, 3), Person2("Bob", 30, true)) | ||
|
||
val ex1json = nrktkt.ninny.obj("foo" ~> "baz", "bar" ~> Seq(1, 2, 3), | ||
"person" ~> nrktkt.ninny.obj( | ||
"name" ~> "Alice", | ||
"age" ~> 27, | ||
"married" ~> false, | ||
)) | ||
val ex2json = io.circe.Json.obj( | ||
"foo" -> io.circe.Json.fromString("baz"), | ||
"bar" -> io.circe.Json.fromValues(Seq(1, 2, 3).map(io.circe.Json.fromInt)), | ||
"person" -> io.circe.Json.obj( | ||
"name" -> io.circe.Json.fromString("Bob"), | ||
"age" -> io.circe.Json.fromInt(30), | ||
"married" -> io.circe.Json.fromBoolean(true), | ||
) | ||
) | ||
|
||
"Circe typeclasses" should "write ninny json" in { | ||
import CirceToNinny._ | ||
|
||
val json = ex1.toSomeJson | ||
json shouldEqual ex1json | ||
} | ||
|
||
it should "read ninny json" in { | ||
import CirceToNinny._ | ||
|
||
val objekt = ex1json.to[Example1].success.value | ||
objekt shouldEqual ex1 | ||
} | ||
|
||
"ninny typeclasses" should "write circe json" in { | ||
import NinnyToCirce._ | ||
val json = ex2.asJson | ||
json shouldEqual ex2json | ||
} | ||
|
||
it should "read circe json" in { | ||
import NinnyToCirce._ | ||
|
||
val objekt = ex2json.as[Example2].toOption | ||
objekt shouldEqual Some(ex2) | ||
} | ||
} |