Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do we really need ==:==? #934

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down Expand Up @@ -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)

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down Expand Up @@ -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]],
Expand Down