@@ -11,11 +11,35 @@ private[chimney] class DslMacroUtils()(using quotes: Quotes) {
1111
1212 private object SelectLike {
1313 def unapply (term : Term ): Option [(Term , String )] = term match {
14- case Select (instance, name) => Some ((instance, name))
15- case Apply (Select (instance, name), Nil ) => Some ((instance, name))
14+ case Select (instance, name) => Some ((instance, name))
15+ case Apply (Select (instance, name), Nil ) => Some ((instance, name))
16+ case Apply (
17+ Apply (TypeApply (Select (Ident (" NamedTuple" ), " apply" ), List (fieldNames, _)), List (instance)),
18+ List (Literal (IntConstant (i)))
19+ ) =>
20+ for {
21+ fieldNameStrings <- tupleUnrollStrings(fieldNames.tpe)
22+ fieldName <- fieldNameStrings.lift(i)
23+ } yield (instance, fieldName)
1624 case Block (_, SelectLike (instance, name)) => Some ((instance, name))
1725 case _ => None
1826 }
27+
28+ // Borrowed from an amazing work by @arainko,
29+ // https://github.com/arainko/ducktape/blob/7984125ffe3493c2e61b72dea799017bb31597f7/ducktape/src/main/scala/io/github/arainko/ducktape/internal/Tuples.scala#L8-L30
30+ private def tupleUnroll (tpe : quoted.Type [? ]): Option [List [TypeRepr ]] = {
31+ // TODO: duplicated from chimney-macro-commons, should be shared
32+ @ scala.annotation.tailrec
33+ def loop (curr : quoted.Type [? ], acc : List [TypeRepr ]): Option [List [reflect.TypeRepr ]] = curr match {
34+ case ' [head *: tail] => loop(quoted.Type .of[tail], TypeRepr .of[head] :: acc)
35+ case ' [EmptyTuple ] => Some (acc)
36+ case other => None
37+ }
38+ loop(tpe, Nil ).map(_.reverse)
39+ }
40+
41+ private def tupleUnrollStrings (tr : TypeRepr ): Option [List [String ]] =
42+ tupleUnroll(tr.asType).map(_.map { case ConstantType (StringConstant (s)) => s })
1943 }
2044
2145 private object IsOptionOf {
0 commit comments