-
-
Save TimWSpence/57f02cb06aa6a193e9485794aa06ee92 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
import java.util.UUID | |
import io.estatico.newtype.Coercible | |
import sangria.marshalling.FromInput.{CoercedScalaResult, InputObjectResult} | |
import sangria.schema._ | |
import sangria.util.tag.@@ | |
import sangria.validation.ValueCoercionViolation | |
import shapeless.{LabelledGeneric, Lazy} | |
import shapeless._ | |
import shapeless.labelled.FieldType | |
import scala.reflect.ClassTag | |
object derivation { | |
def deriveInputType[A](implicit toInput: ToInputType[A]): InputType[toInput.Repr] = toInput.to | |
} | |
case object IDViolation extends ValueCoercionViolation("Invalid ID") | |
sealed trait ToInputType[A] { | |
type Repr | |
def to: InputType[Repr] | |
} | |
object ToInputType extends LowPriorityInstances with LowestPriorityInstances { | |
type Aux[A, R] = ToInputType[A] {type Repr = R} | |
implicit val stringToInputType: ToInputType.Aux[String, String @@ CoercedScalaResult] = new ToInputType[String] { | |
override type Repr = String @@ CoercedScalaResult | |
override def to: InputType[Repr] = StringType | |
} | |
implicit val longToInputType: ToInputType.Aux[Long, Long @@ CoercedScalaResult] = new ToInputType[Long] { | |
override type Repr = Long @@ CoercedScalaResult | |
override def to: InputType[Repr] = LongType | |
} | |
implicit val intToInputType: ToInputType.Aux[Int, Int @@ CoercedScalaResult] = new ToInputType[Int] { | |
override type Repr = Int @@ CoercedScalaResult | |
override def to: InputType[Repr] = IntType | |
} | |
implicit val booleanToInputType: ToInputType.Aux[Boolean, Boolean @@ CoercedScalaResult] = new ToInputType[Boolean] { | |
override type Repr = Boolean @@ CoercedScalaResult | |
override def to: InputType[Repr] = BooleanType | |
} | |
implicit val doubleToInputType: ToInputType.Aux[Double, Double @@ CoercedScalaResult] = new ToInputType[Double] { | |
override type Repr = Double @@ CoercedScalaResult | |
//The Sangria FloatType is actually `InputType[Double]` ¯\_(ツ)_/¯ | |
override def to: InputType[Repr] = FloatType | |
} | |
implicit val uuidToInputType: ToInputType.Aux[UUID, UUID @@ CoercedScalaResult] = new ToInputType[UUID] { | |
override type Repr = UUID @@ CoercedScalaResult | |
override def to: InputType[Repr] = ScalarAlias[UUID, String](StringType, | |
toScalar = _.toString, | |
fromScalar = idString ⇒ try Right(UUID.fromString(idString)) catch { | |
case _: IllegalArgumentException ⇒ Left(IDViolation) | |
}) | |
} | |
implicit def optionToInputType[A, R](implicit toInput: ToInputType.Aux[A, R]): ToInputType.Aux[Option[A], Option[R]] = new ToInputType[Option[A]] { | |
override type Repr = Option[R] | |
override def to: InputType[Repr] = OptionInputType(toInput.to) | |
} | |
implicit def listToInputType[A, R](implicit toInput: ToInputType.Aux[A, R]): ToInputType.Aux[List[A], Seq[R]] = new ToInputType[List[A]] { | |
override type Repr = Seq[R] | |
override def to: InputType[Repr] = ListInputType(toInput.to) | |
} | |
} | |
trait LowPriorityInstances { | |
implicit def newtypeToInputType[A, R1, R2](implicit coerce: Coercible[A, R1], reprToInput: ToInputType.Aux[R1, R2]): ToInputType.Aux[A, R2] = new ToInputType[A] { | |
override type Repr = R2 | |
override def to: InputType[Repr] = reprToInput.to | |
} | |
implicit def seqToInputType[A, R](implicit toInput: ToInputType.Aux[A, R]): ToInputType.Aux[Seq[A], Seq[R]] = new ToInputType[Seq[A]] { | |
override type Repr = Seq[toInput.Repr] | |
override def to: InputType[Repr] = ListInputType(toInput.to) | |
} | |
} | |
trait LowestPriorityInstances { | |
implicit def toInputTypeViaGeneric[A, Repr](implicit gen: LabelledGeneric.Aux[A, Repr], toFieldList: Lazy[ToFieldList[Repr]], tag: ClassTag[A]): ToInputType.Aux[A, A @@ InputObjectResult] = new ToInputType[A] { | |
override type Repr = A @@ InputObjectResult | |
override def to: InputType[Repr] = | |
InputObjectType[A]( | |
name = tag.toString.split("\\.").last, | |
fields = toFieldList.value.to | |
) | |
} | |
} | |
trait ToFieldList[A] { | |
def to: List[InputField[_]] | |
} | |
object ToFieldList { | |
implicit val hnilToFieldList: ToFieldList[HNil] = new ToFieldList[HNil] { | |
override def to: List[InputField[_]] = Nil | |
} | |
implicit def hlistToFieldList[K <: Symbol, R, H, T <: HList]( | |
implicit witness: Witness.Aux[K], | |
headToInput: Lazy[ToInputType.Aux[H, R]], | |
tailToFieldList: ToFieldList[T] | |
): ToFieldList[FieldType[K, H] :: T] = new ToFieldList[FieldType[K, H] :: T] { | |
override def to: List[InputField[_]] = InputField(witness.value.name, headToInput.value.to) :: tailToFieldList.to | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment