no.kodeworks.kvarg.refs.package.scala Maven / Gradle / Ivy
package no.kodeworks.kvarg
import akka.actor.ActorRef
import akka.pattern.ask
import akka.util.Timeout
import cats.syntax.option._
import no.kodeworks.kvarg.message.{Get, GetReplyComplete, GetReplyPatch}
import no.kodeworks.kvarg.patch.Patch
import no.kodeworks.kvarg.util._
import shapeless.ops.hlist.{Fill, Length, LiftAll, Zip}
import shapeless.ops.record.{Keys, SelectAll, UnzipFields}
import shapeless.{HList, IsDistinctConstraint, LabelledGeneric, _}
import scala.collection.immutable
import scala.concurrent.{ExecutionContext, Future}
import scala.language.experimental.macros
import scala.language.{dynamics, existentials}
package object refs {
trait Refs[Model] {
def asMap: Map[Symbol, Option[Any]]
}
object Refs {
def empty[Model] = new Refs[Model] {
override val asMap = Map.empty
}
}
sealed trait RefLookup[Model, Foreign <: HList] {
def apply(p: Model)(implicit to: Timeout, ec: ExecutionContext): Future[Refs[Model]]
def apply(pp: Patch[Model])(implicit to: Timeout, ec: ExecutionContext): Future[Refs[Model]]
}
object RefLookup {
def apply[Model, Foreign <: HList](implicit mkRefLookup: MkRefLookup[Model, Foreign]): mkRefLookup.type = mkRefLookup
def empty[Model, Foreign <: HList] = new RefLookup[Model, Foreign] {
override def apply(p: Model)(implicit to: Timeout, ec: ExecutionContext): Future[Refs[Model]] =
Future.successful(Refs.empty)
override def apply(pp: Patch[Model])(implicit to: Timeout, ec: ExecutionContext): Future[Refs[Model]] =
Future.successful(Refs.empty)
}
}
sealed trait MkRefLookup[Model, Foreign <: HList] {
def model: String
def foreignKeys: Map[Symbol, Symbol]
def apply(serviceLookup: Map[Symbol, ActorRef]): RefLookup[Model, Foreign]
}
implicit def mkRefLookup[
Model
, Foreign <: HList
, ModelFields0 <: HList
, ModelKeys0 <: HList
, ForeignKeys0 <: HList
, Overlap0 <: HList
, ForeignValues0 <: HList
, ForeignValueTypeables <: HList
, ForeignValueToKeys0 <: HList
](implicit
model0: Typeable[Model]
, modelLG0: LabelledGeneric.Aux[Model, ModelFields0]
, modelKeys0: Keys.Aux[ModelFields0, ModelKeys0]
, foreignUnzip0: UnzipFields.Aux[Foreign, ForeignKeys0, ForeignValues0]
, overlap0: SelectAll.Aux[ModelFields0, ForeignKeys0, Overlap0]
, distinctForeignKeys0: IsDistinctConstraint[ForeignKeys0]
, longOrOptionOfLong0: LongOrOptionOfLong[Overlap0]
, foreignValueToKeys: ToKeys.Aux[ForeignValues0, ForeignValueToKeys0]
, foreignValueTypeables: LiftAll.Aux[Typeable, ForeignValues0, ForeignValueTypeables]
, foreignValueToKeysAreSymbols0: LUBConstraint[ForeignValueToKeys0, Symbol]
)
: MkRefLookup[Model, Foreign] =
new MkRefLookup[Model, Foreign] {
override val model = typeableToSimpleName(model0)
override val foreignKeys =
hlistToList[Symbol](foreignUnzip0.keys()).zip(
hlistToList[Symbol](foreignValueToKeys())).toMap
override def apply(serviceLookup: Map[Symbol, ActorRef]) = {
foreignKeys.values.foreach(k => require(serviceLookup.contains(k)))
def lookup(modelForeigns: Map[Symbol, (Long, Typeable[Any])])
(implicit to: Timeout, ec: ExecutionContext) = {
Future.sequence(modelForeigns.map { case (k, (v, tt)) =>
val svc = serviceLookup(foreignKeys(k))
(svc ? Get[Any](v.some)(tt)).mapAll(k -> _.toOption)
}).map(res =>
new Refs[Model] {
override val asMap = res.toMap.mapValues {
case Some(GetReplyComplete(p)) => Some(p)
case Some(GetReplyPatch(pp)) => Some(pp)
case _ => None
}
}
)
}
new RefLookup[Model, Foreign] {
override def apply(p: Model)
(implicit to: Timeout, ec: ExecutionContext)
: Future[Refs[Model]] = {
val modelForeigns = transformLongOptions(hlistToList[Symbol](foreignUnzip0.keys()).zip(
hlistToList[Any](overlap0(modelLG0.to(p))).zip(
hlistToList[Typeable[Any]](foreignValueTypeables.instances)
)))
lookup(modelForeigns)
}
override def apply(pp: Patch[Model])(implicit to: Timeout, ec: ExecutionContext) = {
val modelForeigns = transformLongOptions(hlistToList[Symbol](foreignUnzip0.keys()).zip(
hlistToList[Typeable[Any]](foreignValueTypeables.instances)).flatMap{
case (k, tt) =>
pp.get(k).map(l => (k,(l, tt)))
})
lookup(modelForeigns)
}
}
}
}
trait LongOrOptionOfLong[L <: HList] extends Serializable
trait LongOrOptionOfLongLP {
implicit def hlistInstance0[H, T <: HList](implicit bct: LongOrOptionOfLong[T], ev: H =:= Option[Long]) =
new LongOrOptionOfLong[H :: T] {}
}
object LongOrOptionOfLong extends LongOrOptionOfLongLP {
def apply[L <: HList](implicit lc: LongOrOptionOfLong[L]): LongOrOptionOfLong[L] = lc
implicit def hnilInstance = new LongOrOptionOfLong[HNil] {}
implicit def hlistInstance[H, T <: HList](implicit bct: LongOrOptionOfLong[T], ev: H =:= Long) =
new LongOrOptionOfLong[H :: T] {}
}
trait ToKeys[L <: HList] extends DepFn0 with Serializable {
type Out <: HList
}
object ToKeys {
type Aux[L <: HList, Out0 <: HList] = ToKeys[L] {type Out = Out0}
def apply[L <: HList](implicit toKeys: ToKeys[L]): toKeys.type = toKeys
implicit def hnilToKeys[L <: HNil]: Aux[L, HNil] = new ToKeys[L] {
type Out = HNil
def apply(): Out = HNil
}
implicit def hlistToKeys[V, Rest <: HList]
(implicit
toKeysRest: ToKeys[Rest]
, tt: Typeable[V]
): Aux[V :: Rest, Symbol :: toKeysRest.Out] = new ToKeys[V :: Rest] {
type Out = Symbol :: toKeysRest.Out
def apply(): Out =
Symbol(typeableToSimpleName(tt)) :: toKeysRest()
}
}
trait ToKeysNested[L <: HList] extends DepFn0 with Serializable {
type Out <: HList
}
object ToKeysNested {
type Aux[L <: HList, Out0 <: HList] = ToKeysNested[L] {type Out = Out0}
def apply[L <: HList](implicit toKeysNested: ToKeysNested[L]): toKeysNested.type = toKeysNested
implicit def hnilToKeysNested[L <: HNil]: Aux[L, HNil] = new ToKeysNested[L] {
type Out = HNil
override def apply(): Out = HNil
}
implicit def hlistToKeysNested[V <: HList, Rest <: HList]
(implicit
toKeys: ToKeys[V]
, toKeysNestedRest: ToKeysNested[Rest]
): Aux[V :: Rest, toKeys.Out :: toKeysNestedRest.Out] = new ToKeysNested[V :: Rest] {
override type Out = toKeys.Out :: toKeysNestedRest.Out
def apply(): Out =
toKeys() :: toKeysNestedRest()
}
}
sealed trait ServiceLookup[Domains <: HList, Services] {
def apply(services: Services): Map[Symbol, ActorRef]
}
object ServiceLookup {
def apply[Domains <: HList, Services](implicit serviceLookup: ServiceLookup[Domains, Services]): serviceLookup.type = serviceLookup
implicit def serviceLookup[
Domains <: HList
, Services
, ServicesFields <: HList
, ServicesKeys <: HList
, ServicesValues <: HList
, Len <: Nat
, DomainsToKeys <: HList
, DomainServices <: HList
, FlatDomainServices <: HList
]
(implicit
domainsAreHLists: LUBConstraint[Domains, HList]
, servicesFields: LabelledGeneric.Aux[Services, ServicesFields]
, servicesUnzip: UnzipFields.Aux[ServicesFields, ServicesKeys, ServicesValues]
, servicesKeysAreSymbols: LUBConstraint[ServicesKeys, Symbol]
, servicesAreActorRefs: LUBConstraint[ServicesValues, ActorRef]
, servicesLength: Length.Aux[ServicesFields, Len]
, domainsLength: Length.Aux[Domains, Len]
, domainToKeys: ToKeysNested.Aux[Domains, DomainsToKeys]
, domainServices: Zip.Aux[DomainsToKeys :: ServicesValues :: HNil, DomainServices]
)
: ServiceLookup[Domains, Services] = {
new ServiceLookup[Domains, Services] {
override def apply(services: Services) = {
val servicesValues = servicesUnzip.values(servicesFields.to(services))
val domainServices0 = domainServices(domainToKeys() :: servicesValues :: HNil)
hlistToList[(HList, ActorRef)](domainServices0).flatMap {
case (keys, service) => hlistToList[Symbol](keys).map(_ -> service)
}.toMap
}
}
}
}
private def transformLongOptions(mf: Seq[(Symbol, (Any, Typeable[Any]))]): Map[Symbol, (Long, Typeable[Any])] =
mf.flatMap {
case (k, (l: Long, tt)) => (k, (l, tt)).some
case (k, (Some(l: Long), tt)) => (k, (l, tt)).some
case (_, (None, _)) => None
}.toMap
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy