Open oyvindberg opened 1 year ago
I gave this a go some time ago and failed miserably, albeit my knowledge of macros goes as far as I can google things. Also things might have changed in the compiler since then. If you can make this work it'd be awesome :)
I think I can make it work. Progress so far:
Calling the macro like this:
type A = Int | String | Types.QueryHeroArgs
Foo.typeAliasSchema[A]
gives this data:
{
starwars.generated.Foo.TypeAndSchema.apply[scala.Int]("scala.Int", caliban.schema.Schema.intSchema)
starwars.generated.Foo.TypeAndSchema.apply[scala.Predef.String]("java.lang.String", caliban.schema.Schema.stringSchema)
starwars.generated.Foo.TypeAndSchema.apply[starwars.generated.Types.QueryHeroArgs]("starwars.generated.Types.QueryHeroArgs", starwars.generated.Types.QueryHeroArgs.derived$SemiAuto)
}
It's obviously not in the correct shape yet, but I managed to pick apart the type union and resolve Schema
for each type.
Code so far:
case class TypeAndSchema[T](typeRef: String, schema: Schema[Any, T])
inline def typeAliasSchema[T]: TypeAndSchema[?] = ${ mirrorFieldsImpl[T] }
def mirrorFieldsImpl[T: Type](using Quotes): Expr[TypeAndSchema[?]] = {
import quotes.reflect.* // Import `Tree`, `TypeRepr`, `Symbol`, `Position`, .....
def rec[TT](using tpe: Type[TT]): List[Expr[TypeAndSchema[?]]] = TypeRepr.of(using tpe).dealias match {
case OrType(l, r) =>
quotes.reflect.report.warning(s"union ${l.show} | ${r.show}")
rec(using l.asType.asInstanceOf[Type[Any]]) ++ rec(using r.asType.asInstanceOf[Type[Any]])
case otherRepr =>
val otherString = otherRepr.show
val expr: Expr[TypeAndSchema[TT]] =
Expr.summon[Schema[Any, TT]] match {
case Some(found) =>
'{ TypeAndSchema[TT](${ Expr(otherString) }, ${found}) }
case None =>
quotes.reflect.report.errorAndAbort(s"Couldn't resolve Schema[Any, $otherString]")
}
List(expr)
}
val exprs = rec[T]
val expr = Expr.block(exprs.init, exprs.last)
quotes.reflect.report.errorAndAbort(expr.show)
expr
}
this library likely contains what we need https://github.com/iRevive/union-derivation
With namespacing gone after #1925, we're quite close to being able to output union types as union types.
The remaining issue is basically this:
That's true. But, and I'm on very thin ice here, I think we can write a macro which pattern matches on the union type, picks out all the members and
summonAll
sSchema
s for them.Then we need to generate some code like this:
Originally posted by @oyvindberg in https://github.com/ghostdogpr/caliban/issues/1925#issuecomment-1750114106