-
-
Save vil1/7f28126db61ee9d6d0a9407da1b042fa to your computer and use it in GitHub Desktop.
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
package scalaz | |
package schema | |
package generic | |
import shapeless._, labelled._ | |
import monocle.Iso | |
trait AutoSchema extends SchemaModule { | |
trait SchemaOf[A] extends DepFn0 { | |
type Out <: Schema[_] | |
} | |
def schema[A](implicit A: SchemaOf.Aux[A, A]): Schema[A] = A() | |
trait LowPrioritySchemaOf { | |
implicit def schemaOfCaseClass[C, R <: HList, S]( | |
implicit lgen: LabelledGeneric.Aux[C, R], | |
R: SchemaOf.Aux[R, S], | |
C: Constructor[S, R] | |
): SchemaOf.Aux[C, C] = new SchemaOf[C] { | |
type Out = Schema[C] | |
def apply: Schema[C] = | |
record(R(), C.iso.composeIso(Iso[R, C](lgen.from)(lgen.to)))( | |
new Schema.LabelledProduct[R.Out] {} | |
) | |
} | |
implicit def schemaOfHCons[A, A1, T <: HList, S]( | |
implicit A: SchemaOf.Aux[A, A1], | |
T: SchemaOf.Aux[T, S] | |
): SchemaOf.Aux[A :: T, (A1, S)] = new SchemaOf[A :: T] { | |
type Out = Schema[(A1, S)] | |
def apply: Schema[(A1, S)] = A() :*: T() | |
} | |
implicit def schemaOfSealedTrait[T, R <: Coproduct, S]( | |
implicit lgen: LabelledGeneric.Aux[T, R], | |
R: SchemaOf.Aux[R, S], | |
T: Constructor[S, R] | |
): SchemaOf.Aux[T, T] = new SchemaOf[T] { | |
type Out = Schema[T] | |
def apply: Schema[T] = | |
union(R(), T.iso.composeIso(Iso[R, T](lgen.from)(lgen.to)))( | |
new Schema.LabelledSum[R.Out] {} | |
) | |
} | |
implicit def schemaOfCCons[A, A1, T <: Coproduct, S]( | |
implicit A: SchemaOf.Aux[A, A1], | |
T: SchemaOf.Aux[T, S] | |
): SchemaOf.Aux[A :+: T, A1 \/ S] = new SchemaOf[A :+: T] { | |
type Out = Schema[A1 \/ S] | |
def apply: Schema[A1 \/ S] = A() :+: T() | |
} | |
implicit def schemaOfBranch[K <: Symbol, V]( | |
implicit K: Witness.Aux[K], | |
V: SchemaOf.Aux[V, V], | |
ev: Liskov[String, SumTermId] | |
): SchemaOf.Aux[FieldType[K, V], V] = new SchemaOf[FieldType[K, V]] { | |
type Out = Schema[V] | |
def apply: Schema[V] = ev(K.value.name) -+>: V() | |
} | |
} | |
object SchemaOf extends LowPrioritySchemaOf { | |
type Aux[A, A0] = SchemaOf[A] { type Out = Schema[A0] } | |
def apply[T](implicit T: SchemaOf[T]): SchemaOf[T] = T | |
implicit def schemaOfField[K <: Symbol, V]( | |
implicit K: Witness.Aux[K], | |
V: SchemaOf.Aux[V, V], | |
ev: Liskov[String, ProductTermId] | |
): SchemaOf.Aux[FieldType[K, V], V] = new SchemaOf[FieldType[K, V]] { | |
type Out = Schema[V] | |
def apply: Schema[V] = ev(K.value.name) -*>: V() | |
} | |
implicit def schemaOfSingletonHList[A, A1]( | |
implicit A: SchemaOf.Aux[A, A1] | |
): SchemaOf.Aux[A :: HNil, A1] = new SchemaOf[A :: HNil] { | |
type Out = Schema[A1] | |
def apply: Schema[A1] = A() | |
} | |
implicit def schemaOfSingletonCoproduct[A, A1]( | |
implicit A: SchemaOf.Aux[A, A1] | |
): SchemaOf.Aux[A :+: CNil, A1] = new SchemaOf[A :+: CNil] { | |
type Out = Schema[A1] | |
def apply: Schema[A1] = A() | |
} | |
} | |
trait Constructor[A, A0] { | |
def iso: Iso[A, A0] | |
} | |
trait LowPriorityConstructor { | |
implicit def constructorOfHCons[K, A, T <: HList, S]( | |
implicit T: Constructor[S, T] | |
): Constructor[(A, S), FieldType[K, A] :: T] = new Constructor[(A, S), FieldType[K, A] :: T] { | |
def iso: Iso[(A, S), FieldType[K, A] :: T] = { | |
val tail = T.iso | |
Iso[(A, S), FieldType[K, A] :: T]( | |
p => p._1.asInstanceOf[FieldType[K, A]] :: tail.get(p._2) | |
)(l => (l.head, tail.reverseGet(l.tail))) | |
} | |
} | |
implicit def constructorOfCCons[K, A, T <: Coproduct, S]( | |
implicit T: Constructor[S, T] | |
): Constructor[A \/ S, FieldType[K, A] :+: T] = new Constructor[A \/ S, FieldType[K, A] :+: T] { | |
def iso: Iso[A \/ S, FieldType[K, A] :+: T] = | |
Iso[A \/ S, FieldType[K, A] :+: T] { | |
case -\/(a) => Inl(a.asInstanceOf[FieldType[K, A]]) | |
case \/-(s) => Inr(T.iso.get(s)) | |
} { | |
case Inl(a) => -\/(a) | |
case Inr(s) => \/-(T.iso.reverseGet(s)) | |
} | |
} | |
} | |
object Constructor extends LowPriorityConstructor { | |
def apply[A, S](implicit C: Constructor[A, S]): Constructor[A, S] = C | |
implicit def constructorOfSingletonHList[K, A]: Constructor[A, FieldType[K, A] :: HNil] = | |
new Constructor[A, FieldType[K, A] :: HNil] { | |
def iso: Iso[A, FieldType[K, A] :: HNil] = | |
Iso[A, FieldType[K, A] :: HNil]( | |
_.asInstanceOf[FieldType[K, A]] :: HNil | |
)(_.head.asInstanceOf[A]) | |
} | |
implicit def constructorOfSingletonCoproduct[K, A]: Constructor[A, FieldType[K, A] :+: CNil] = | |
new Constructor[A, FieldType[K, A] :+: CNil] { | |
def iso: Iso[A, FieldType[K, A] :+: CNil] = | |
Iso[A, FieldType[K, A] :+: CNil](a => Inl(a.asInstanceOf[FieldType[K, A]])) { | |
case Inl(a) => a | |
case Inr(_) => throw new Exception("Impossible") | |
} | |
} | |
} | |
} | |
object demo { | |
val autoModule = new JsonModule with AutoSchema { | |
type Prim[A] = JsonSchema.Prim[A] | |
type ProductTermId = String | |
type SumTermId = String | |
} | |
import JsonSchema.{ Prim => _, _ } | |
import autoModule._, SchemaOf._ | |
implicit def schemaOfString: SchemaOf.Aux[String, String] = new SchemaOf[String] { | |
type Out = Schema[String] | |
def apply: Schema[String] = prim(JsonSchema.JsonString) | |
} | |
implicit def schemaOfBoolean: SchemaOf.Aux[Boolean, Boolean] = new SchemaOf[Boolean] { | |
type Out = Schema[Boolean] | |
def apply: Schema[Boolean] = prim(JsonSchema.JsonBool) | |
} | |
sealed trait Base | |
case class Foo(name: String) extends Base | |
case class Bar(active: Boolean) extends Base | |
implicit val primToEncoderNT = new (Prim ~> Encoder) { | |
def apply[A](fa: Prim[A]): Encoder[A] = { a => | |
fa match { | |
case JsonNumber => a.toString | |
case JsonBool => a.toString | |
case JsonString => s""""$a"""" | |
case JsonNull => "null" | |
} | |
} | |
} | |
val baseToJson: Encoder[Base] = schema[Base].to[Encoder] | |
val json = baseToJson(Foo("Merry Christmas 🎅🎁 !")) | |
// scalaz.schema.generic.demo.autoModule.JSON = {"Foo":{"name":"Merry Christmas 🎅🎁 !"}} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment