Skip to content

Commit

Permalink
Merge pull request #68 from kubukoz/stdlib
Browse files Browse the repository at this point in the history
  • Loading branch information
kubukoz committed Aug 10, 2022
2 parents a5db07f + d4403b6 commit ec170dc
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 4 deletions.
1 change: 1 addition & 0 deletions core/src/main/resources/META-INF/smithy/manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.smithy
1 change: 1 addition & 0 deletions core/src/main/resources/META-INF/smithy/std.smithy
85 changes: 85 additions & 0 deletions core/src/main/scala/playground/DynamicServiceProxy.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package playground

import cats.MonadThrow
import cats.implicits._
import smithy4s.Document
import smithy4s.Endpoint
import smithy4s.Service
import smithy4s.schema.Schema

class DynamicServiceProxy[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _]](
service: Service[Alg, Op]
) {

def tryProxy[AlgStatic[_[_, _, _, _, _]], OpStatic[_, _, _, _, _], F[_]: MonadThrow](
interp: smithy4s.Monadic[AlgStatic, F]
)(
implicit serviceStatic: Service[AlgStatic, OpStatic]
): Option[smithy4s.Interpreter[Op, F]] =
Option.when(service.id == serviceStatic.id)(proxy(interp)(serviceStatic))

def proxy[AlgStatic[_[_, _, _, _, _]], OpStatic[_, _, _, _, _], F[_]: MonadThrow](
interp: smithy4s.Monadic[AlgStatic, F]
)(
serviceStatic: Service[AlgStatic, OpStatic]
): smithy4s.Interpreter[Op, F] = {
val grp = serviceStatic.endpoints.groupBy(_.id).fmap(_.head)

type Proxy[I, E, O, SE, EO] = I => F[O]

def makeProxy[A, B](schemaIn: Schema[A], schemaOut: Schema[B]): A => F[B] = {
val inputEncoder = Document.Encoder.fromSchema(schemaIn)
val outputDecoder = Document.Decoder.fromSchema(schemaOut)

in => outputDecoder.decode(inputEncoder.encode(in)).liftTo[F]
}

val endpointMapping =
new (smithy4s.Transformation[Endpoint[Op, *, *, *, *, *], Proxy]) {
private val trans = serviceStatic.asTransformation(interp)

private def applyWithStatic[I, E, O, SI, SO, STI, STE, STO, STSI, STSO](
endpoint: Endpoint[Op, I, E, O, SI, SO],
endpointStatic: Endpoint[OpStatic, STI, STE, STO, STSI, STSO],
): I => F[O] = {
val mapInput = makeProxy(endpoint.input, endpointStatic.input)
val mapOutput = makeProxy(endpointStatic.output, endpoint.output)

def errorMapper[A]: Throwable => F[A] =
endpointStatic.errorable match {
case None => _.raiseError[F, A]
case Some(errorableStatic) =>
val errorable = endpoint.errorable.get // should be there at this point
val mapError = makeProxy(errorableStatic.error, errorable.error)

e =>
errorableStatic.liftError(e) match {
case None => e.raiseError[F, A]
case Some(liftedStatic) =>
mapError(liftedStatic)
.flatMap(errorable.unliftError(_).raiseError[F, A])
}
}

input =>
mapInput(input)
.map(endpointStatic.wrap)
.flatMap(trans(_))
.handleErrorWith(errorMapper)
.flatMap(mapOutput)
}

def apply[I, E, O, SI, SO](endpoint: Endpoint[Op, I, E, O, SI, SO]): I => F[O] =
applyWithStatic(endpoint, grp(endpoint.id))
}
.precompute(service.endpoints)

new smithy4s.Interpreter[Op, F] {
def apply[I, E, O, SI, SO](op: Op[I, E, O, SI, SO]): F[O] = {
val (input, endpoint) = service.endpoint(op)
endpointMapping(endpoint)(input)
}
}
}

}
31 changes: 28 additions & 3 deletions core/src/main/scala/playground/run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.http4s.Uri
import org.http4s.client.Client
import org.http4s.implicits._
import playground._
import playground.plugins.PlaygroundPlugin
import playground.smithyql.InputNode
import playground.smithyql.OperationName
import playground.smithyql.QualifiedIdentifier
Expand All @@ -35,7 +36,8 @@ import smithy4s.dynamic.DynamicSchemaIndex
import smithy4s.http4s.SimpleProtocolBuilder
import smithy4s.http4s.SimpleRestJsonBuilder
import smithy4s.schema.Schema
import playground.plugins.PlaygroundPlugin
import playground.std.Stdlib
import playground.std.StdlibRuntime

trait CompiledInput {
type _Op[_, _, _, _, _]
Expand Down Expand Up @@ -249,7 +251,7 @@ object Runner {
}
}

def forSchemaIndex[F[_]: Concurrent: Defer: std.Console](
def forSchemaIndex[F[_]: StdlibRuntime: Concurrent: Defer: std.Console](
dsi: DynamicSchemaIndex,
client: Client[F],
baseUri: F[Uri],
Expand Down Expand Up @@ -293,7 +295,11 @@ object Runner {
}
}

def forService[Alg[_[_, _, _, _, _]], Op[_, _, _, _, _], F[_]: Concurrent: Defer: std.Console](
def forService[
Alg[_[_, _, _, _, _]],
Op[_, _, _, _, _],
F[_]: StdlibRuntime: Concurrent: Defer: std.Console,
](
service: Service[Alg, Op],
client: Client[F],
baseUri: F[Uri],
Expand Down Expand Up @@ -336,6 +342,24 @@ object Runner {
.toIor
.toIorNel

private def stdlibRunner: IorNel[Issue, smithy4s.Interpreter[Op, F]] =
smithy4s
.checkProtocol(
service,
Stdlib.getTag,
)
.leftMap(e => Issue.InvalidProtocol(e.protocolTag.id, serviceProtocols): Issue)
.toIor
.toIorNel *> {
val proxy = new DynamicServiceProxy[Alg, Op](service)

proxy
.tryProxy(StdlibRuntime[F].random)
.orElse(proxy.tryProxy(StdlibRuntime[F].clock))
.toRightIor(Issue.Other(new Throwable("unknown standard service")))
.toIorNel
}

val awsInterpreter: IorNel[Issue, smithy4s.Interpreter[Op, F]] = AwsClient
.prepare(service)
.as {
Expand Down Expand Up @@ -365,6 +389,7 @@ object Runner {
awsInterpreter,
)
.concat(plugins.flatMap(_.http4sBuilders).map(simpleFromBuilder))
.append(stdlibRunner)
.map(
_.map { interpreter => q =>
perform[q.I, q.E, q.O](
Expand Down
39 changes: 39 additions & 0 deletions core/src/main/scala/playground/std/StdlibRuntime.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package playground.std

import cats.Functor
import cats.effect.std.UUIDGen
import cats.implicits._
import smithy4s.Timestamp

trait StdlibRuntime[F[_]] {
def random: Random[F]
def clock: Clock[F]
}

object StdlibRuntime {
def apply[F[_]](implicit F: StdlibRuntime[F]): StdlibRuntime[F] = F

def instance[F[_]: cats.effect.Clock: UUIDGen: Functor]: StdlibRuntime[F] =
new StdlibRuntime[F] {

val random: Random[F] =
new playground.std.Random[F] {
def nextUUID(): F[NextUUIDOutput] = UUIDGen[F].randomUUID.map(NextUUIDOutput(_))
}

val clock: Clock[F] =
new Clock[F] {

def currentTimestamp(
): F[CurrentTimestampOutput] = cats
.effect
.Clock[F]
.realTimeInstant
.map(Timestamp.fromInstant)
.map(CurrentTimestampOutput.apply)

}

}

}
34 changes: 34 additions & 0 deletions core/src/main/smithy/std.smithy
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace playground.std
use smithy4s.api#UUID

@trait(selector: "service")
@protocolDefinition
structure stdlib {}

@stdlib
service Random {
operations: [NextUUID]
}

operation NextUUID {
output: NextUUIDOutput
}

structure NextUUIDOutput {
@required
value: UUID
}

@stdlib
service Clock {
operations: [CurrentTimestamp]
}

operation CurrentTimestamp {
output: CurrentTimestampOutput
}

structure CurrentTimestampOutput {
@required
value: Timestamp
}
2 changes: 1 addition & 1 deletion lsp/src/main/scala/playground/lsp/BuildLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ object BuildLoader {
dependencies = loaded.config.mavenDependencies.combineAll,
repositories = loaded.config.mavenRepositories.combineAll,
transformers = Nil,
discoverModels = false,
discoverModels = true,
localJars = Nil,
)
._2
Expand Down
3 changes: 3 additions & 0 deletions lsp/src/main/scala/playground/lsp/ServerBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import playground.TextDocumentManager
import smithy4s.aws.AwsEnvironment
import smithy4s.aws.http4s.AwsHttp4sBackend
import smithy4s.aws.kernel.AwsRegion
import playground.std.StdlibRuntime

trait ServerBuilder[F[_]] {
def build(buildInfo: BuildLoader.Loaded, loader: ServerLoader[F]): F[LanguageServer[F]]
Expand All @@ -30,6 +31,8 @@ object ServerBuilder {
Uri.fromString(_).leftMap(_.message)
)

implicit val stdlibRuntime: StdlibRuntime[F] = StdlibRuntime.instance[F]

EmberClientBuilder
.default[F]
.build
Expand Down

0 comments on commit ec170dc

Please sign in to comment.