From 618183d4e68765db7503316e0a2046a00c02ec75 Mon Sep 17 00:00:00 2001 From: Yann Simon Date: Mon, 28 Oct 2024 11:50:00 +0100 Subject: [PATCH] fix ambiguous implicits in scala 3 reproduce and fix https://github.com/sangria-graphql/sangria/pull/1139#issuecomment-2441154074 --- .../scala/sangria/marshalling/FromInput.scala | 8 +- .../sangria/marshalling/FromInputSpec.scala | 73 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/test/scala/sangria/marshalling/FromInputSpec.scala diff --git a/src/main/scala/sangria/marshalling/FromInput.scala b/src/main/scala/sangria/marshalling/FromInput.scala index a5367f3..b69fcaf 100644 --- a/src/main/scala/sangria/marshalling/FromInput.scala +++ b/src/main/scala/sangria/marshalling/FromInput.scala @@ -9,7 +9,7 @@ trait FromInput[Val] { def fromResult(node: marshaller.Node): Val } -object FromInput { +object FromInput extends LowPriorityFromInput { private object ScalarFromInput extends FromInput[Any] { val marshaller: CoercedScalaResultMarshaller = CoercedScalaResultMarshaller.default def fromResult(node: marshaller.Node): marshaller.Node = node @@ -59,10 +59,14 @@ object FromInput { implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ev.asInstanceOf[FromInput[Option[T]]] - implicit def seqInput[T](implicit ev: FromInput[T]): SeqFromInput[T] = new SeqFromInput[T](ev) implicit def iterableInput[T](implicit ev: FromInput[T]): IterableFromInput[T, Iterable] = new IterableFromInput[T, Iterable](ev) trait CoercedScalaResult trait InputObjectResult } + +trait LowPriorityFromInput { + import FromInput.SeqFromInput + implicit def seqInput[T](implicit ev: FromInput[T]): SeqFromInput[T] = new SeqFromInput[T](ev) +} diff --git a/src/test/scala/sangria/marshalling/FromInputSpec.scala b/src/test/scala/sangria/marshalling/FromInputSpec.scala new file mode 100644 index 0000000..3a935f6 --- /dev/null +++ b/src/test/scala/sangria/marshalling/FromInputSpec.scala @@ -0,0 +1,73 @@ +package sangria.marshalling + +import org.scalatest.wordspec.AnyWordSpec + +class FromInputSpec extends AnyWordSpec { + + "FromInput" should { + "provide default FromInput for Map[String, Any]" in { + val fromInput = implicitly[FromInput[Map[String, Any]]] + + assert(fromInput != null) + } + + "provide default FromInput for Seq" in { + import FromInputSpec.FromInputStringInstance.stringFromInput + + val fromInput = implicitly[FromInput[Seq[String]]] + + assert(fromInput != null) + } + + "provide default FromInput for Iterable" in { + import FromInputSpec.FromInputStringInstance.stringFromInput + + val fromInput = implicitly[FromInput[Iterable[String]]] + + assert(fromInput != null) + } + + "provide default FromInput for Option" in { + import FromInputSpec.FromInputStringInstance.stringFromInput + + val fromInput = implicitly[FromInput[Option[String]]] + + assert(fromInput != null) + } + + "provide default FromInput for Option of Seq" in { + import FromInputSpec.FromInputStringInstance.stringFromInput + + val fromInput = implicitly[FromInput[Option[Seq[String]]]] + + assert(fromInput != null) + } + + "resolve covariant Seq" in { + import FromInputSpec.InputType._ + import FromInputSpec.FromInputStringInstance.stringFromInput + + val fromInput = instance(OptionInputType(SeqInputType(StringInputType))) + + assert(fromInput != null) + } + } +} + +object FromInputSpec { + object FromInputStringInstance { + implicit val stringFromInput: FromInput[String] = new FromInput[String] { + override val marshaller: ResultMarshaller = CoercedScalaResultMarshaller.default + override def fromResult(node: marshaller.Node): String = "hello" + } + } + + trait InputType[+T] + object InputType { + case class OptionInputType[T](t: InputType[T]) extends InputType[Option[T]] + case class SeqInputType[T](t: InputType[T]) extends InputType[Seq[T]] + case object StringInputType extends InputType[String] + + def instance[T](t: InputType[T])(implicit fromInput: FromInput[T]): FromInput[T] = fromInput + } +}