diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/CassandraTable.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/CassandraTable.scala index fa0cf263d..78b3f5693 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/CassandraTable.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/CassandraTable.scala @@ -25,7 +25,7 @@ import com.outworkers.phantom.builder.query.{RootCreateQuery, _} import com.outworkers.phantom.column.AbstractColumn import com.outworkers.phantom.connectors.{KeySpace, RootConnector} import com.outworkers.phantom.keys.SASIIndex -import com.outworkers.phantom.macros.{==:==, SingleGeneric, TableHelper} +import com.outworkers.phantom.macros.{SingleGeneric, TableHelper} import org.slf4j.{Logger, LoggerFactory} import shapeless.{Generic, HList, HNil} @@ -117,13 +117,12 @@ abstract class CassandraTable[T <: CassandraTable[T, R], R]( * @tparam V1 The type of the input. * @return A default input query. */ - def store[V1, Repr <: HList, HL, Out <: HList](input: V1)( + def store[V1, Repr <: HList, HL](input: V1)( implicit keySpace: KeySpace, thl: TableHelper.Aux[T, R, Repr], gen: Generic.Aux[V1, HL], - sg: SingleGeneric.Aux[V1, Repr, HL, Out], - ev: Out ==:== Repr - ): InsertQuery.Default[T, R] = thl.store(instance, (sg to input).asInstanceOf[Repr]) + sg: SingleGeneric[V1, Repr, HL] + ): InsertQuery.Default[T, R] = thl.store(instance, sg to input) final def delete()(implicit keySpace: KeySpace): DeleteQuery.Default[T, R] = DeleteQuery[T, R](instance) diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/HListEqs.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/HListEqs.scala deleted file mode 100644 index ad8948d29..000000000 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/HListEqs.scala +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 - 2020 Outworkers Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.outworkers.phantom.macros - -import com.outworkers.phantom.macros.toolbelt.{HListHelpers, WhiteboxToolbelt} -import shapeless.HList - -import scala.annotation.implicitNotFound -import scala.reflect.macros.whitebox - -/** - * A special implicitly materialized typeclass that compares HLists for equality. - * This also prints more useful information to the end user if the HLists don't match. - * However, it is required because the standard [[=:=]] type class in Scala - * is not able to see through an HList generated as an abstract type member of another - * implicitly materialized type class. - * @tparam LL The left [[shapeless.HList]] type. - * @tparam RR The right [[shapeless.HList]] type. - */ -@implicitNotFound(msg = "HList ${LL} does not contain the same types as ${RR}") -trait ==:==[LL <: HList, RR <: HList] - -object ==:== { - - def apply[LL <: HList, RR <: HList](implicit ev: LL ==:== RR): ==:==[LL, RR] = ev - - implicit def materialize[ - LL <: HList, - RR <: HList - ]: ==:==[LL, RR] = macro EqsMacro.materialize[LL, RR] -} - - -class EqsMacro(val c: whitebox.Context) extends WhiteboxToolbelt with HListHelpers { - - import c.universe._ - - protected[this] val macroPkg = q"_root_.com.outworkers.phantom.macros" - - - def mkEqs(left: Type, right: Type): Tree = { - if (left =:= right) { - evalTree { - q"""new $macroPkg.==:==[$left, $right] {}""" - } - - } else { - val debugString = s"Types ${showHList(left)} did not equal ${showHList(right)}" - error(debugString) - abort( debugString) - } - } - - def materialize[LL <: HList : c.WeakTypeTag, RR <: HList : c.WeakTypeTag]: Tree = { - val left = weakTypeOf[LL] - val right = weakTypeOf[RR] - - memoize[(Type, Type), Tree]( - WhiteboxToolbelt.specialEqsCache - )(Tuple2(left, right), { case (t, s) => mkEqs(t, s)}) - } - - -} diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/SingleGeneric.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/SingleGeneric.scala index 097a8702d..0df6898d2 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/SingleGeneric.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/SingleGeneric.scala @@ -15,11 +15,9 @@ */ package com.outworkers.phantom.macros -import com.outworkers.phantom.macros.toolbelt.{HListHelpers, WhiteboxToolbelt} -import shapeless.Generic +import shapeless.{::, Generic, HNil} import scala.annotation.implicitNotFound -import scala.reflect.macros.whitebox /** * A very dirty typeclass used to duct type single argument calls to the store varargs autotupled method. @@ -49,83 +47,32 @@ import scala.reflect.macros.whitebox @implicitNotFound(msg = "The type you're trying to store ${T} should match either ${Store} or ${GenR}") trait SingleGeneric[T, Store, GenR] extends Serializable { /** The generic representation type for {T}, which will be composed of {Coproduct} and {HList} types */ - type Repr + type Repr = Store /** Convert an instance of the concrete type to the generic value representation */ - def to(t: T)(implicit gen: Generic[T]) : Repr + def to(t: T) : Repr /** Convert an instance of the generic representation to an instance of the concrete type */ - def from(r: Repr)(implicit gen: Generic[T]) : T + def from(r: Repr) : T } -object SingleGeneric { +trait LowPrioritySingleGeneric { + implicit def single[T, HL](implicit gen: Generic.Aux[T, HL]): SingleGeneric[T, T :: HNil, HL] = + new SingleGeneric[T, T :: HNil, HL] { + def to(source: T): T :: HNil = source :: HNil - def apply[T, Store, HL](implicit ev: SingleGeneric[T, Store, HL]): SingleGeneric[T, Store, HL] = ev - - implicit def materialize[ - T, - Store, - HL - ]: SingleGeneric[T, Store, HL] = macro SingleGenericMacro.materialize[T, Store, HL] - - type Aux[T, Store, HL, Repr0] = SingleGeneric[T, Store, HL] { type Repr = Repr0 } + def from(hl: T :: HNil): T = hl.head + } } +object SingleGeneric extends LowPrioritySingleGeneric { -class SingleGenericMacro(val c: whitebox.Context) extends HListHelpers with WhiteboxToolbelt { - import c.universe._ - - protected[this] val macroPkg = q"_root_.com.outworkers.phantom.macros" - private[this] def genericTpe(tpe: Type): Tree = tq"_root_.shapeless.Generic[$tpe]" - - def mkGeneric(tpe: Type, store: Type, generic: Type): Tree = { - val res = mkHListType(tpe :: Nil) - val genTpe = genericTpe(tpe) - - val tree = if (store =:= generic) { - info(s"Generic implementation using Shapeless for ${printType(tpe)}") - q""" - new $macroPkg.SingleGeneric[$tpe, $store, $generic] { - type Repr = $generic - - def to(source: $tpe)(implicit gen: ${genericTpe(tpe)}): $generic = { - (gen to source).asInstanceOf[$generic] - } - - def from(hl: $generic)(implicit gen: $genTpe): $tpe = (gen from hl.asInstanceOf[gen.Repr]) - } - """ - } else if (store =:= res) { - info(s"Single generic implementation using coalesced HLists for ${printType(tpe)}") - - q""" - new $macroPkg.SingleGeneric[$tpe, $store, $generic] { - type Repr = $res + def apply[T, Store, HL](implicit ev: SingleGeneric[T, Store, HL]): SingleGeneric[T, Store, HL] = ev - def to(source: $tpe)(implicit gen: $genTpe): $res = source :: _root_.shapeless.HNil + implicit def generic[T, HL](implicit gen: Generic.Aux[T, HL]): SingleGeneric[T, HL, HL] = + new SingleGeneric[T, HL, HL] { + def to(source: T): HL = gen to source - def from(hl: $res)(implicit gen: $genTpe): $tpe = hl.apply(_root_.shapeless.Nat._0) - } - """ - } else { - val debugString = s"Unable to derive store type for ${printType(tpe)}, expected ${showHList(generic)} or ${showHList(store)}" - error(debugString) - abort(debugString) + def from(hl: HL): T = gen from hl } - - evalTree(tree) - } - - def materialize[T : c.WeakTypeTag, Store : c.WeakTypeTag, HL : c.WeakTypeTag]: Tree = { - val tableType = weakTypeOf[T] - val store = weakTypeOf[Store] - val generic = weakTypeOf[HL] - - - memoize[(Type, Type, Type), Tree]( - WhiteboxToolbelt.singeGenericCache - )(Tuple3(tableType, store, generic), { case (t, s, g) => - mkGeneric(t, s, g) - }) - } -} +} \ No newline at end of file diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/TableHelper.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/TableHelper.scala index 38681991c..829b5ce78 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/TableHelper.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/TableHelper.scala @@ -54,8 +54,9 @@ trait TableHelper[T <: CassandraTable[T, R], R] extends Serializable { object TableHelper { implicit def fieldsMacro[ T <: CassandraTable[T, R], - R - ]: TableHelper[T, R] = macro TableHelperMacro.materialize[T, R] + R, + Repr <: HList + ]: Aux[T, R, Repr] = macro TableHelperMacro.materialize[T, R] def apply[T <: CassandraTable[T, R], R](implicit ev: TableHelper[T, R]): TableHelper[T, R] = ev @@ -315,7 +316,7 @@ class TableHelperMacro(override val c: whitebox.Context) extends WhiteboxToolbel * Alternatively, this will return an unimplemented ??? method, provided a correct * definition could not be inferred. */ - def extractor[T](tableTpe: Type, recordTpe: Type, columns: List[Symbol]): TableDescriptor = { + def extractor(tableTpe: Type, recordTpe: Type, columns: List[Symbol]): TableDescriptor = { val recordMembers = extractRecordMembers(recordTpe) val colFields = extractColumnMembers(tableTpe, columns) diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/BlackboxToolbelt.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/BlackboxToolbelt.scala index 146f2aaca..8786c4e50 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/BlackboxToolbelt.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/BlackboxToolbelt.scala @@ -29,8 +29,6 @@ private[phantom] object BlackboxToolbelt { final val primitiveCache: Cache = new Cache() final val tableHelperCache: Cache = new Cache() - final val singeGenericCache: Cache = new Cache() - final val specialEqsCache: Cache = new Cache() } private[phantom] trait BlackboxToolbelt { diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/WhiteboxToolbelt.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/WhiteboxToolbelt.scala index 40ffae68d..6187b195b 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/WhiteboxToolbelt.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/macros/toolbelt/WhiteboxToolbelt.scala @@ -30,8 +30,6 @@ private[phantom] object WhiteboxToolbelt { final val ddHelperCache: Cache = new Cache() final val bindHelperCache: Cache = new Cache() final val tableHelperCache: Cache = new Cache() - final val singeGenericCache: Cache = new Cache() - final val specialEqsCache: Cache = new Cache() } private[phantom] trait WhiteboxToolbelt { diff --git a/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/QueryContext.scala b/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/QueryContext.scala index d88c018aa..ac314823a 100644 --- a/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/QueryContext.scala +++ b/phantom-dsl/src/main/scala/com/outworkers/phantom/ops/QueryContext.scala @@ -24,7 +24,7 @@ import com.outworkers.phantom.builder.query.prepared.{ExecutablePreparedQuery, E import com.outworkers.phantom.builder.query._ import com.outworkers.phantom.connectors.KeySpace import com.outworkers.phantom.database.Database -import com.outworkers.phantom.macros.{==:==, SingleGeneric, TableHelper} +import com.outworkers.phantom.macros.{SingleGeneric, TableHelper} import com.outworkers.phantom.{CassandraTable, ResultSet, Row} import shapeless.{Generic, HList} @@ -305,29 +305,26 @@ abstract class QueryContext[P[_], F[_], Timeout]( blockAwait(table.autocreate(keySpace).future(), timeout) } - def storeRecord[V1, Repr <: HList, HL <: HList, Out <: HList](input: V1)( + def storeRecord[V1, Repr <: HList, HL <: HList](input: V1)( implicit keySpace: KeySpace, session: Session, thl: TableHelper.Aux[T, R, Repr], gen: Generic.Aux[V1, HL], - sg: SingleGeneric.Aux[V1, Repr, HL, Out], - ctx: ExecutionContext, - ev: Out ==:== Repr + sg: SingleGeneric[V1, Repr, HL], + ctx: ExecutionContext ): F[ResultSet] = promiseInterface.adapter.fromGuava(table.store(input).executableQuery) def storeRecords[ M[X] <: IterableOnce[X], V1, Repr <: HList, - HL <: HList, - Out <: HList + HL <: HList ](inputs: M[V1])( implicit keySpace: KeySpace, session: Session, thl: TableHelper.Aux[T, R, Repr], gen: Generic.Aux[V1, HL], - sg: SingleGeneric.Aux[V1, Repr, HL, Out], - ev: Out ==:== Repr, + sg: SingleGeneric[V1, Repr, HL], ctx: ExecutionContext, cbfEntry: Factory[ExecutableCqlQuery, M[ExecutableCqlQuery]], cbfB: BuildFrom[M[ExecutableCqlQuery], ExecutableCqlQuery, M[ExecutableCqlQuery]],