Skip to content

Commit

Permalink
JsonValue type as type member
Browse files Browse the repository at this point in the history
  • Loading branch information
nrktkt authored and Nathan Fischer committed Jan 7, 2023
1 parent 88868c7 commit 31a6524
Show file tree
Hide file tree
Showing 14 changed files with 122 additions and 89 deletions.
2 changes: 1 addition & 1 deletion USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ You can use ninny's dynamic update syntax easly to replace values way down in th
repo='kag0/ninny-json'
lang='scala'
file='ninny/test/src-2/io/github/kag0/ninny/userguide/DomainTo.scala'
lines='7:51'
lines='7:53'
></sauce-code>

# Converting JSON to domain objects
Expand Down
4 changes: 2 additions & 2 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import $ivy.`com.lihaoyi::mill-contrib-bloop:$MILL_VERSION`
import mill.define.{Segment, Segments}
import $file.forProductN

val `2.12` = "2.12.15"
val `2.13` = "2.13.8"
val `2.12` = "2.12.17"
val `2.13` = "2.13.10"
val `3` = "3.1.0"

val scalaTest = ivy"org.scalatest::scalatest:3.2.10"
Expand Down
12 changes: 8 additions & 4 deletions forProductN.sc
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ def generateProductToJson = {
.map(j => s"a${j}ToJson: ToJson[A$j]")
.mkString(", ")

out ++= "): ToSomeJsonObject[Target] = target => {\n"
out ++= "): ToSomeJsonObject[Target] = ToJson(target => {\n"
out ++= "val ("
out ++= (0 until i).map(j => s"a$j").mkString(", ")
out ++= ") = f(target)\n"
out ++= "obj("
out ++= (0 until i).map(j => s"(nameA$j, a$j)").mkString(", ")
out ++= ")\n}\n"
out ++= ")\n})\n"
}

out += '}'
Expand Down Expand Up @@ -65,6 +65,7 @@ def generateProductFromJson = {
def generateProductToAndFromJson = {
val out = new StringBuilder()
out ++= "package nrktkt.ninny\n"
out ++= "import scala.language.existentials\n"
out ++= "import nrktkt.ninny.ast._\n"
out ++= "trait ProductToAndFromJson {\n"
for (i <- 1 to 22) {
Expand All @@ -84,16 +85,19 @@ def generateProductToAndFromJson = {
.map(j => s"a${j}ToAndFromJson: ToAndFromJson[A$j]")
.mkString(", ")

out ++= ") = new ToAndFromJson[Target] {\n"
out ++= ") = {\n"

val namesCsv = (0 until i).map(j => s"nameA$j").mkString(", ")
out ++= s"val _toJson: ToSomeJson[Target] = ToJson.forProduct$i($namesCsv)(fTo)\n"
out ++= s"val _fromJson: FromJson[Target] = FromJson.forProduct$i($namesCsv)(fFrom)\n"

out ++= "new ToAndFromJson[Target] {\n"
out ++= "type Json = _toJson.Json\n"
out ++= """
def from(json: Option[JsonValue]) = _fromJson.from(json)
def toSome(target: Target) = _toJson.toSome(target)
"""
out ++= "}\n"
out ++= "}}\n"
}

out += '}'
Expand Down
4 changes: 2 additions & 2 deletions ninny/src-2/nrktkt/ninny/ToJsonAutoImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ trait ToJsonAutoImpl {
toJson: Lazy[ToSomeJsonObject[UpdatedRecord]]
): ToJsonAuto[A] = {
val replacedNames = replaceNames(fields.keys, annotations())
new ToJsonAuto[A](a => {
new ToJsonAuto[A](ToJson(a => {
val record = generic.to(a)
val updatedRecord = zipWithNames(
replacedNames :: fields.values(record) :: HNil
)
toJson.value.toSome(updatedRecord)
})
}))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ trait VersionSpecificToJsonInstances {
}
}

implicit val hNilToJson: ToSomeJsonObject[HNil] = _ => JsonObject(Map.empty)
implicit val hNilToJson: ToSomeJsonObject[HNil] =
ToJson(_ => JsonObject(Map.empty))
}
10 changes: 4 additions & 6 deletions ninny/src/io/github/kag0/ninny/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ package object ninny
extends VersionSpecificPackage
with nrktkt.ninny.MagneticMethods {
val Json = nrktkt.ninny.Json
type ToJson[A] = nrktkt.ninny.ToJsonValue[A, JsonValue]
type ToJson[A] = nrktkt.ninny.ToJson[A]
val ToJson = nrktkt.ninny.ToJson
type ToJsonValue[A, J <: JsonValue] = nrktkt.ninny.ToJsonValue[A, J]
val ToJsonValue = nrktkt.ninny.ToJsonValue
type ToJsonObject[A] = nrktkt.ninny.ToJsonValue[A, JsonObject]
type ToSomeJson[A] = nrktkt.ninny.ToSomeJsonValue[A, JsonValue]
type ToSomeJsonObject[A] = nrktkt.ninny.ToSomeJsonValue[A, JsonObject]
type ToJsonValue[A] = nrktkt.ninny.ToJson[A]
type ToSomeJson[A] = nrktkt.ninny.ToSomeJson[A]
type ToSomeJsonObject[A] = nrktkt.ninny.ToSomeJsonObject[A]
type FromJson[A] = nrktkt.ninny.FromJson[A]
val FromJson = nrktkt.ninny.FromJson
type ToAndFromJson[A] = nrktkt.ninny.ToAndFromJson[A]
Expand Down
1 change: 1 addition & 0 deletions ninny/src/nrktkt/ninny/ToAndFromJson.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ object ToAndFromJson extends ProductToAndFromJson {
fromJson: FromJson[A]
): ToAndFromJson[A] =
new ToAndFromJson[A] {
type Json = toJson.Json
def toSome(a: A) = toJson.toSome(a)
def from(maybeJson: Option[JsonValue]) = fromJson.from(maybeJson)
}
Expand Down
44 changes: 34 additions & 10 deletions ninny/src/nrktkt/ninny/ToJson.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,52 @@ package nrktkt.ninny

import nrktkt.ninny.ast.JsonValue

trait ToJsonValue[A, +Json <: JsonValue] {
def to(a: A): Option[Json]
trait ToJson[A] {
type Json <: JsonValue

def contramap[B](f: B => A): ToJsonValue[B, Json] = b => to(f(b))
def to(a: A): Option[Json]
def contramap[B](f: B => A): ToJson[B] = ToJson((b: B) => to(f(b)))
}

object ToJsonValue extends ToJsonInstances

object ToJson extends ProductToJson {
object ToJson
extends ToJsonInstances
with ProductToJson
with ToSomeJsonConstructor {

def apply[A: ToJson]: ToJson[A] = implicitly[ToJson[A]]
type Aux[A, +J <: JsonValue] = ToJson[A] {
type Json <: J
}

def apply[A, Json <: JsonValue](fn: A => Option[Json]): ToJsonValue[A, Json] =
(a: A) => fn(a)
def apply[A: ToJson]: ToJson[A] = implicitly[ToJson[A]]

def apply[A, Json <: JsonValue](fn: A => Json): ToSomeJsonValue[A, Json] =
(a: A) => fn(a)
def apply[A, J <: JsonValue](
fn: A => Option[J]
): ToJson.Aux[A, J] = new ToJson[A] {
type Json = J
def to(a: A): Option[Json] = fn(a)
}

def auto[A: ToJsonAuto] = implicitly[ToJsonAuto[A]].toJson
}

trait ToSomeJsonValue[A, +Json <: JsonValue] extends ToJsonValue[A, Json] {
trait ToSomeJson[A] extends ToJson[A] {
def toSome(a: A): Json
override def to(a: A) = Some(toSome(a))
}

object ToSomeJson extends ToSomeJsonConstructor {
type Aux[A, +J <: JsonValue] = ToSomeJson[A] {
type Json <: J
}
}

private[ninny] trait ToSomeJsonConstructor {
def apply[A, J <: JsonValue](
fn: A => J
): ToSomeJson[A] { type Json = J } = new ToSomeJson[A] {
type Json = J
def toSome(a: A): J = fn(a)
}
}
69 changes: 35 additions & 34 deletions ninny/src/nrktkt/ninny/ToJsonInstances.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,72 +10,73 @@ import scala.collection.compat.immutable.ArraySeq
trait ToJsonInstances
extends VersionSpecificToJsonInstances
with LowPriorityToJsonInstances {
implicit val stringToJson: ToSomeJsonValue[String, JsonString] =
implicit val stringToJson: ToSomeJson[String] =
ToJson(JsonString(_))

implicit val booleanToJson: ToSomeJsonValue[Boolean, JsonBoolean] =
JsonBoolean(_)
implicit val booleanToJson: ToSomeJson[Boolean] =
ToJson(JsonBoolean(_))

implicit val nullToJson: ToSomeJsonValue[Null, JsonNull.type] = _ => JsonNull
implicit val nullToJson: ToSomeJson[Null] = ToJson(_ => JsonNull)

implicit val bigDecimalToJson: ToSomeJsonValue[BigDecimal, JsonDecimal] =
JsonDecimal(_)
implicit val bigDecimalToJson: ToSomeJson[BigDecimal] =
ToJson(JsonDecimal(_))

implicit val bigIntToJson: ToSomeJsonValue[BigInt, JsonDecimal] = i =>
JsonDecimal(BigDecimal(i, MathContext.UNLIMITED))
implicit val bigIntToJson: ToSomeJson.Aux[BigInt, JsonDecimal] =
ToJson(i => JsonDecimal(BigDecimal(i, MathContext.UNLIMITED)))

implicit val longToJson: ToSomeJsonValue[Long, JsonDecimal] = l =>
JsonDecimal(BigDecimal(l))
implicit val longToJson: ToSomeJson.Aux[Long, JsonDecimal] =
ToJson(l => JsonDecimal(BigDecimal(l)))

implicit val arraySeqToJson: ToSomeJsonValue[ArraySeq[Byte], JsonBlob] =
JsonBlob(_)
implicit val arraySeqToJson: ToSomeJson.Aux[ArraySeq[Byte], JsonBlob] =
ToJson(JsonBlob(_))

implicit def arrayToJson[A: ToSomeJson]
: ToSomeJsonValue[Array[A], JsonArray] =
arr => {
implicit def arrayToJson[A: ToSomeJson]: ToSomeJson.Aux[Array[A], JsonArray] =
ToJson(arr => {
val builder = immutable.Seq.newBuilder[JsonValue]
builder.sizeHint(arr.length)
for (i <- 0 until arr.length) { builder += (arr(i).toSomeJson) }
JsonArray(builder.result())
}
})

implicit def jsonToJson[J <: JsonValue]: ToSomeJson[J] = j => j
implicit def jsonToJson[J <: JsonValue]: ToSomeJson[J] = ToJson(j => j)

/** represents unit as an empty JSON array. because a tuple is a heterogeneous
* list; (5, "foo") => [5, "foo"] and unit is an empty tuple; () => []
*/
implicit val unitToJson: ToSomeJson[Unit] = _ => JsonArray(Nil)
implicit val unitToJson: ToSomeJson[Unit] = ToJson(_ => JsonArray(Nil))

implicit def mapToJson[A: ToJson]: ToSomeJson[Map[String, A]] =
m =>
ToJson(m =>
JsonObject(m.collect(Function.unlift { case (k, v) =>
v.toJson.map(k -> _)
}))
)

implicit def optionToJson[A: ToJson]: ToJson[Option[A]] =
a => a.flatMap(ToJson[A].to(_))
ToJson((a: Option[A]) => a.flatMap(ToJson[A].to(_)))

implicit val noneToJson: ToJson[None.type] = _ => None
implicit def someToJson[A: ToJson]: ToJson[Some[A]] = optionToJson[A].to(_)
implicit def someToSomeJson[A: ToSomeJson]: ToSomeJson[Some[A]] =
_.value.toSomeJson
implicit val noneToJson: ToJson[None.type] = _ => None
implicit def someToJson[A: ToJson]: ToJson[Some[A]] =
ToJson((some: Some[A]) => optionToJson[A].to(some))
implicit def someToSomeJson[A: ToSomeJson]: ToSomeJson[Some[A]] =
ToJson(_.value.toSomeJson)

implicit def leftToJson[L: ToJson, R]: ToJson[Left[L, R]] = _.value.toJson
implicit def rightToJson[L, R: ToJson]: ToJson[Right[L, R]] = _.value.toJson
implicit def eitherToJson[L: ToJson, R: ToJson]: ToJson[Either[L, R]] =
_.fold(_.toJson, _.toJson)

implicit val instantToJson: ToSomeJson[Instant] =
i => JsonNumber(i.getEpochSecond.toDouble)
ToJson(i => JsonNumber(i.getEpochSecond.toDouble))

implicit val offsetDateTimeToJson: ToSomeJson[OffsetDateTime] =
time => JsonString(time.toString)
ToJson(time => JsonString(time.toString))

implicit val zonedDateTimeToJson: ToSomeJson[ZonedDateTime] =
time => JsonString(time.toString)
ToJson(time => JsonString(time.toString))

implicit val uuidToJson: ToSomeJsonValue[UUID, JsonString] =
uuid => JsonString(uuid.toString)
implicit val uuidToJson: ToSomeJson.Aux[UUID, JsonString] =
ToJson(uuid => JsonString(uuid.toString))

}
object ToJsonInstances extends ToJsonInstances
Expand All @@ -84,15 +85,15 @@ trait LowPriorityToJsonInstances {
implicit def iterableToJson[I, A](implicit
ev: I <:< Iterable[A],
toJson: ToSomeJson[A]
): ToSomeJsonValue[I, JsonArray] =
xs => {
): ToSomeJson.Aux[I, JsonArray] =
ToJson(xs => {
val builder = immutable.Seq.newBuilder[JsonValue]
xs.foreach(builder += _.toSomeJson)
JsonArray(builder.result())
}
})

implicit def numericToJson[A: Numeric]: ToSomeJsonValue[A, JsonDouble] =
a => JsonDouble(implicitly[Numeric[A]].toDouble(a))
implicit def numericToJson[A: Numeric]: ToSomeJson.Aux[A, JsonDouble] =
ToJson(a => JsonDouble(implicitly[Numeric[A]].toDouble(a)))
}

class ToJsonAuto[A](val toJson: ToSomeJsonObject[A]) extends AnyVal
Expand Down
15 changes: 7 additions & 8 deletions ninny/src/nrktkt/ninny/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ package object ninny {
}

implicit class AnySyntax[A](val _a: A) extends AnyVal {
def toJson[Json <: JsonValue](implicit toJson: ToJsonValue[A, Json]) =
def toJson(implicit toJson: ToJson[A]): Option[toJson.Json] =
toJson.to(_a)

def toSomeJson[Json <: JsonValue](implicit
toJson: ToSomeJsonValue[A, Json]
) = toJson.toSome(_a)
def toSomeJson(implicit
toJson: ToSomeJson[A]
): toJson.Json = toJson.toSome(_a)

}

implicit class ArrowSyntax(val s: String) extends AnyVal {
Expand All @@ -70,10 +71,8 @@ package object ninny {
def ~>[A: ToJson](a: A) = s -> JsonMagnet(a)
}

type ToJson[A] = ToJsonValue[A, JsonValue]
type ToJsonObject[A] = ToJsonValue[A, JsonObject]
type ToSomeJson[A] = ToSomeJsonValue[A, JsonValue]
type ToSomeJsonObject[A] = ToSomeJsonValue[A, JsonObject]
type ToJsonObject[A] = ToJson.Aux[A, JsonObject]
type ToSomeJsonObject[A] = ToSomeJson.Aux[A, JsonObject]

// @deprecated(
// message =
Expand Down
Loading

0 comments on commit 31a6524

Please sign in to comment.