Skip to content

Instantly share code, notes, and snippets.

@pshirshov
Last active April 4, 2018 23:17
Show Gist options
  • Save pshirshov/ffdd7558030082580ebbd896f626ecaf to your computer and use it in GitHub Desktop.
Save pshirshov/ffdd7558030082580ebbd896f626ecaf to your computer and use it in GitHub Desktop.
IDL: services model
package com.github.pshirshov.izumi.idealingua.runtime.services
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
import scala.language.{higherKinds, implicitConversions}
//--------------------------------------------------------------------------
// Runtime: unopinionated part
trait ServiceResult[R[_]] {
@inline def map[A, B](r: R[A])(f: A => B): R[B]
@inline def flatMap[A, B](r: R[A])(f: A => R[B]): R[B]
@inline def pure[A](v: => A): R[A]
}
object ServiceResult {
type Id[T] = T
@inline implicit def toOps[R[_], A](value: R[A]): ServiceResultOps[R, A] = new ServiceResultOps[R, A](value)
class ServiceResultOps[R[_], A](val value: R[A]) extends AnyVal {
@inline def map[B](f: A => B)(implicit serviceResult: ServiceResult[R]): R[B] = serviceResult.map(value)(f)
@inline def flatMap[B](f: A => R[B])(implicit serviceResult: ServiceResult[R]): R[B] = serviceResult.flatMap(value)(f)
}
implicit object ServiceResultId extends ServiceResult[Id] {
@inline override def map[A, B](r: Id[A])(f: A => B) = f(r)
@inline override def flatMap[A, B](fa: Id[A])(f: A => Id[B]): Id[B] = f(fa)
@inline override def pure[A](v: => A): Id[A] = v
}
implicit object ServiceResultOption extends ServiceResult[Option] {
@inline override def map[A, B](r: Option[A])(f: A => B): Option[B] = r.map(f)
@inline override def flatMap[A, B](r: Option[A])(f: A => Option[B]): Option[B] = r.flatMap(f)
@inline override def pure[A](v: => A): Option[A] = Option(v)
}
implicit object ServiceResultTry extends ServiceResult[Try] {
@inline override def map[A, B](r: Try[A])(f: A => B): Try[B] = r.map(f)
@inline override def flatMap[A, B](r: Try[A])(f: A => Try[B]): Try[B] = r.flatMap(f)
@inline override def pure[A](v: => A): Try[A] = Try(v)
}
implicit def toServiceResultFutureOps(implicit ec: ExecutionContext): ServiceResultFuture = new ServiceResultFuture
class ServiceResultFuture(implicit ec: ExecutionContext) extends ServiceResult[Future] {
@inline override def map[A, B](r: Future[A])(f: A => B): Future[B] = r.map(f)
@inline override def flatMap[A, B](r: Future[A])(f: A => Future[B]): Future[B] = r.flatMap(f)
@inline override def pure[A](v: => A): Future[A] = Future(v)
}
}
trait ServiceResultTransformer[R1[_], R2[_]] {
def transform[A](r: R1[A]): R2[A]
}
object ServiceResultTransformer {
implicit val transformId: ServiceResultTransformer[ServiceResult.Id, ServiceResult.Id] = new ServiceResultTransformerId[ServiceResult.Id]
implicit val transformTry: ServiceResultTransformer[Try, Try] = new ServiceResultTransformerId[Try]
implicit val transformOption: ServiceResultTransformer[Option, Option] = new ServiceResultTransformerId[Option]
implicit val transformFuture: ServiceResultTransformer[Future, Future] = new ServiceResultTransformerId[Future]
class ServiceResultTransformerId[R[_]] extends ServiceResultTransformer[R, R] {
override def transform[A](r: R[A]): R[A] = r
}
}
trait WithResultType[R[_]] {
type Result[T] = R[T]
}
trait WithResult[R[_]] extends WithResultType[R] {
protected def _ServiceResult: ServiceResult[R]
protected def _Result[T](value: => T): R[T] = _ServiceResult.pure(value)
}
trait Marshaller[Value, Marshalled] {
def encode(v: Value): Marshalled
}
trait Unmarshaller[Marshalled, Value] {
def decode(v: Marshalled): Value
}
trait Transport[RequestWire, ResponseWire] {
def send(v: RequestWire): ResponseWire
}
trait TransportMarshallers[RequestWire, Request, ResponseWire, Response] {
val requestUnmarshaller: Unmarshaller[RequestWire, Request]
val requestMarshaller: Marshaller[Request, RequestWire]
val responseMarshaller: Marshaller[Response, ResponseWire]
val responseUnmarshaller: Unmarshaller[ResponseWire, Response]
}
trait Dispatcher[In, Out, R[_]] extends WithResultType[R] {
def dispatch(input: In): Result[Out]
}
trait Receiver[In, Out, R[_]] extends WithResultType[R] {
def receive(input: In): Result[Out]
}
//--------------------------------------------------------------------------
// Runtime: opinionated part
class ServerReceiver[RequestWire, Request, ResponseWire, Response, R[_] : ServiceResult]
(
dispatcher: Dispatcher[Request, Response, R]
, bindings: TransportMarshallers[RequestWire, Request, ResponseWire, Response]
) extends Receiver[RequestWire, ResponseWire, R] with WithResult[R] {
override protected def _ServiceResult: ServiceResult[R] = implicitly
def receive(request: RequestWire): R[ResponseWire] = {
import ServiceResult._
_Result(bindings.requestUnmarshaller.decode(request))
.flatMap(dispatcher.dispatch)
.map(bindings.responseMarshaller.encode)
}
}
class ClientDispatcher[RequestWire, Request, ResponseWire, Response, R[_] : ServiceResult]
(
transport: Transport[RequestWire, R[ResponseWire]]
, bindings: TransportMarshallers[RequestWire, Request, ResponseWire, Response]
) extends Dispatcher[Request, Response, R] with WithResult[R] {
override protected def _ServiceResult: ServiceResult[R] = implicitly
def dispatch(input: Request): Result[Response] = {
import ServiceResult._
_Result(bindings.requestMarshaller.encode(input))
.flatMap(transport.send)
.map(bindings.responseUnmarshaller.decode)
}
}
//--------------------------------------------------------------------------
// Generated part
trait GreeterService[R[_]] extends WithResultType[R] {
def greet(name: String, surname: String): Result[String]
}
trait GreeterServiceWrapped[R[_]] extends WithResultType[R] {
import GreeterServiceWrapped._
def greet(input: GreetInput): Result[GreetOutput]
}
object GreeterServiceWrapped {
sealed trait GreeterServiceInput
case class GreetInput(name: String, surname: String) extends GreeterServiceInput
sealed trait GreeterServiceOutput
case class GreetOutput(value: String) extends GreeterServiceOutput
trait GreeterServiceDispatcherPacking[R[_]] extends GreeterService[R] with WithResult[R] {
def dispatcher: Dispatcher[GreeterServiceInput, GreeterServiceOutput, R]
def greet(name: String, surname: String): Result[String] = {
val packed = GreetInput(name, surname)
val dispatched = dispatcher.dispatch(packed)
_ServiceResult.map(dispatched) {
case o: GreetOutput =>
o.value
}
}
}
object GreeterServiceDispatcherPacking {
class Impl[R[_] : ServiceResult](val dispatcher: Dispatcher[GreeterServiceInput, GreeterServiceOutput, R]) extends GreeterServiceDispatcherPacking[R] {
override protected def _ServiceResult: ServiceResult[R] = implicitly
}
}
trait GreeterServiceDispatcherUnpacking[R[_]] extends GreeterServiceWrapped[R] with Dispatcher[GreeterServiceInput, GreeterServiceOutput, R] with WithResult[R] {
def service: GreeterService[R]
override def greet(input: GreetInput): Result[GreetOutput] = {
val result = service.greet(input.name, input.surname)
_ServiceResult.map(result)(GreetOutput)
}
def dispatch(input: GreeterServiceInput): Result[GreeterServiceOutput] = {
input match {
case v: GreetInput =>
_ServiceResult.map(greet(v))(v => v) // upcast
}
}
}
object GreeterServiceDispatcherUnpacking {
class Impl[R[_] : ServiceResult](val service: GreeterService[R]) extends GreeterServiceDispatcherUnpacking[R] {
override protected def _ServiceResult: ServiceResult[R] = implicitly
}
}
}
//--------------------------------------------------------------------------
// setup context and use
trait AbstractGreeterServer[R[_]] extends GreeterService[R] with WithResult[R] {
override def greet(name: String, surname: String): Result[String] = _Result {
s"Hi, $name $surname!"
}
}
object AbstractGreeterServer {
class Impl[R[_] : ServiceResult] extends AbstractGreeterServer[R] {
override protected def _ServiceResult: ServiceResult[R] = implicitly
}
}
class TrivialAppTransport[I, O, R[_]](server: Receiver[I, O, R]) extends Transport[I, R[O]] {
def send(v: I): R[O] = server.receive(v)
}
class PseudoNetwork[I, O, R[_], RT[_]](transport: Transport[I, R[O]])(implicit converter: ServiceResultTransformer[R, RT]) extends Transport[I, RT[O]] {
def send(v: I): RT[O] = {
val sent = transport.send(v)
converter.transform(sent)
}
}
import GreeterServiceWrapped._
object Test {
type GSMarshaler = TransportMarshallers[GreeterServiceInput, GreeterServiceInput, GreeterServiceOutput, GreeterServiceOutput]
class PseudoMarshallers extends GSMarshaler {
override val requestUnmarshaller: Unmarshaller[GreeterServiceInput, GreeterServiceInput] = (v: GreeterServiceInput) => v
override val requestMarshaller: Marshaller[GreeterServiceInput, GreeterServiceInput] = (v: GreeterServiceInput) => v
override val responseMarshaller: Marshaller[GreeterServiceOutput, GreeterServiceOutput] = (v: GreeterServiceOutput) => v
override val responseUnmarshaller: Unmarshaller[GreeterServiceOutput, GreeterServiceOutput] = (v: GreeterServiceOutput) => v
}
class FailingMarshallers extends GSMarshaler {
override val requestUnmarshaller: Unmarshaller[GreeterServiceInput, GreeterServiceInput] = (v: GreeterServiceInput) => ???
override val requestMarshaller: Marshaller[GreeterServiceInput, GreeterServiceInput] = (v: GreeterServiceInput) => ???
override val responseMarshaller: Marshaller[GreeterServiceOutput, GreeterServiceOutput] = (v: GreeterServiceOutput) => ???
override val responseUnmarshaller: Unmarshaller[GreeterServiceOutput, GreeterServiceOutput] = (v: GreeterServiceOutput) => ???
}
class SimpleDemo[R[_] : ServiceResult]
(
marshalling: GSMarshaler
) {
val service = new AbstractGreeterServer.Impl[R]
val serverDispatcher = new GreeterServiceWrapped.GreeterServiceDispatcherUnpacking.Impl(service)
val server = new ServerReceiver(serverDispatcher, marshalling)
val appTransport = new TrivialAppTransport(server)
val clientDispatcher = new ClientDispatcher(appTransport, marshalling)
val client = new GreeterServiceWrapped.GreeterServiceDispatcherPacking.Impl(clientDispatcher)
}
class ConvertingDemo[R[_] : ServiceResult, RT[_] : ServiceResult]
(
marshalling: GSMarshaler
)
(
implicit converter: ServiceResultTransformer[RT, R]
, converter1: ServiceResultTransformer[R, RT]
) {
val service = new AbstractGreeterServer.Impl[R]
val serverDispatcher = new GreeterServiceWrapped.GreeterServiceDispatcherUnpacking.Impl(service)
val server = new ServerReceiver(serverDispatcher, marshalling)
val serverAppTransport = new TrivialAppTransport(server)
val serverNetwork: Transport[GreeterServiceInput, RT[GreeterServiceOutput]] = new PseudoNetwork[GreeterServiceInput, GreeterServiceOutput, R, RT](serverAppTransport)
val clientNetwork: Transport[GreeterServiceInput, R[GreeterServiceOutput]] = new PseudoNetwork[GreeterServiceInput, GreeterServiceOutput, RT, R](serverNetwork)
val clientDispatcher = new ClientDispatcher(clientNetwork, marshalling)
val client = new GreeterServiceWrapped.GreeterServiceDispatcherPacking.Impl(clientDispatcher)
}
private def testSimple(marshalling: GSMarshaler): Unit = {
{
val demo = new SimpleDemo[Try](marshalling)
val result = demo.client.greet("John", "Doe")
println(result)
}
{
import ExecutionContext.Implicits._
val demo = new SimpleDemo[Future](marshalling)
val result = demo.client.greet("John", "Doe")
Thread.sleep(100)
println(result)
}
{
val demo = new SimpleDemo[Option](marshalling)
val result = demo.client.greet("John", "Doe")
println(result)
}
}
private def testConverting(marshalling: GSMarshaler): Unit = {
import ServiceResultTransformer._
{
val demo = new ConvertingDemo[Try, Try](marshalling)
val result = demo.client.greet("John", "Doe")
println(result)
}
{
import ExecutionContext.Implicits._
val demo = new ConvertingDemo[Future, Future](marshalling)
val result = demo.client.greet("John", "Doe")
Thread.sleep(100)
println(result)
}
{
val demo = new ConvertingDemo[Option, Option](marshalling)
val result = demo.client.greet("John", "Doe")
println(result)
}
}
def main(args: Array[String]): Unit = {
testSimple(new PseudoMarshallers())
testConverting(new PseudoMarshallers())
println(Try(testSimple(new FailingMarshallers())))
println(Try(testConverting(new FailingMarshallers())))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment