Skip to content

Commit

Permalink
integrations: scaladocs (#642)
Browse files Browse the repository at this point in the history
  • Loading branch information
fwbrasil authored Sep 13, 2024
1 parent 76eb1d5 commit efd0976
Show file tree
Hide file tree
Showing 7 changed files with 397 additions and 0 deletions.
143 changes: 143 additions & 0 deletions kyo-cache/shared/src/main/scala/kyo/Cache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,30 @@ import com.github.benmanes.caffeine
import com.github.benmanes.caffeine.cache.Caffeine
import java.util.concurrent.TimeUnit

/** A caching utility class that provides memoization functionality via Caffeine.
*
* Each memoized function created by this cache has its own isolated keyspace. This means that different memoized functions, even if they
* have the same input types, will not interfere with each other's cached results. The isolation is achieved by including a reference to
* the cache instance itself in the key, along with the input value.
*
* @param store
* The underlying cache store
*/
class Cache(private[kyo] val store: Store):

/** Memoizes a function with a single argument.
*
* @tparam A
* The input type
* @tparam B
* The output type (must have a Flat instance)
* @tparam S
* The effect type
* @param f
* The function to memoize
* @return
* A memoized version of the input function
*/
def memo[A, B: Flat, S](
f: A => B < S
)(using Frame): A => B < (Async & S) =
Expand Down Expand Up @@ -39,20 +62,71 @@ class Cache(private[kyo] val store: Store):
}
}

/** Memoizes a function with two arguments.
*
* @tparam T1
* The first input type
* @tparam T2
* The second input type
* @tparam S
* The effect type
* @tparam B
* The output type (must have a Flat instance)
* @param f
* The function to memoize
* @return
* A memoized version of the input function
*/
def memo2[T1, T2, S, B: Flat](
f: (T1, T2) => B < S
)(using Frame): (T1, T2) => B < (Async & S) =
val m = memo[(T1, T2), B, S](f.tupled)
(t1, t2) => m((t1, t2))
end memo2

/** Memoizes a function with three arguments.
*
* @tparam T1
* The first input type
* @tparam T2
* The second input type
* @tparam T3
* The third input type
* @tparam S
* The effect type
* @tparam B
* The output type (must have a Flat instance)
* @param f
* The function to memoize
* @return
* A memoized version of the input function
*/
def memo3[T1, T2, T3, S, B: Flat](
f: (T1, T2, T3) => B < S
)(using Frame): (T1, T2, T3) => B < (Async & S) =
val m = memo[(T1, T2, T3), B, S](f.tupled)
(t1, t2, t3) => m((t1, t2, t3))
end memo3

/** Memoizes a function with four arguments.
*
* @tparam T1
* The first input type
* @tparam T2
* The second input type
* @tparam T3
* The third input type
* @tparam T4
* The fourth input type
* @tparam S
* The effect type
* @tparam B
* The output type (must have a Flat instance)
* @param f
* The function to memoize
* @return
* A memoized version of the input function
*/
def memo4[T1, T2, T3, T4, S, B: Flat](
f: (T1, T2, T3, T4) => B < S
)(using Frame): (T1, T2, T3, T4) => B < (Async & S) =
Expand All @@ -62,25 +136,94 @@ class Cache(private[kyo] val store: Store):
end Cache

object Cache:

/** The type of the underlying cache store. */
type Store = caffeine.cache.Cache[Any, Promise[Nothing, Any]]

/** A builder class for configuring Cache instances.
*
* @param b
* The underlying Caffeine builder
*/
case class Builder(private[kyo] val b: Caffeine[Any, Any]) extends AnyVal:
/** Sets the maximum size of the cache.
*
* @param v
* The maximum number of entries the cache may contain
* @return
* An updated Builder
*/
def maxSize(v: Int): Builder =
copy(b.maximumSize(v))

/** Specifies that cache should use weak references for keys.
*
* @return
* An updated Builder
*/
def weakKeys(): Builder =
copy(b.weakKeys())

/** Specifies that cache should use weak references for values.
*
* @return
* An updated Builder
*/
def weakValues(): Builder =
copy(b.weakValues())

/** Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry's
* creation, or the most recent replacement of its value.
*
* @param d
* The length of time after an entry is created that it should be automatically removed
* @return
* An updated Builder
*/
def expireAfterAccess(d: Duration): Builder =
copy(b.expireAfterAccess(d.toMillis, TimeUnit.MILLISECONDS))

/** Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry's
* creation, the most recent replacement of its value, or its last access.
*
* @param d
* The length of time after an entry is last accessed that it should be automatically removed
* @return
* An updated Builder
*/
def expireAfterWrite(d: Duration): Builder =
copy(b.expireAfterWrite(d.toMillis, TimeUnit.MILLISECONDS))

/** Sets the minimum total size for the internal data structures.
*
* @param v
* The minimum total size for the internal data structures
* @return
* An updated Builder
*/
def initialCapacity(v: Int) =
copy(b.initialCapacity(v))

/** Specifies that active entries are eligible for automatic refresh once a fixed duration has elapsed after the entry's creation,
* or the most recent replacement of its value.
*
* @param d
* The duration after which an entry should be considered stale
* @return
* An updated Builder
*/
def refreshAfterWrite(d: Duration) =
copy(b.refreshAfterWrite(d.toMillis, TimeUnit.MILLISECONDS))

end Builder

/** Initializes a new Cache instance with the given configuration.
*
* @param f
* A function that configures the Cache using a Builder
* @return
* A new Cache instance wrapped in an IO effect
*/
def init(f: Builder => Builder)(using Frame): Cache < IO =
IO {
new Cache(
Expand Down
75 changes: 75 additions & 0 deletions kyo-caliban/src/main/scala/kyo/Resolvers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,99 @@ import zio.ZEnvironment
import zio.ZIO
import zio.stream.ZStream

/** Effect for interacting with Caliban GraphQL resolvers. */
opaque type Resolvers <: (Abort[CalibanError] & ZIOs) = Abort[CalibanError] & ZIOs

object Resolvers:

private given StreamConstructor[Nothing] =
(_: ZStream[Any, Throwable, Byte]) => throw new Throwable("Streaming is not supported")

/** Runs a GraphQL server with default NettyKyoServer configuration.
*
* @param v
* The HttpInterpreter to be used
* @param Frame
* Implicit Frame parameter
* @return
* A NettyKyoServerBinding wrapped in ZIOs and Abort effects
*/
def run[A, S](v: HttpInterpreter[Any, CalibanError] < (Resolvers & S))(
using Frame
): NettyKyoServerBinding < (ZIOs & Abort[CalibanError] & S) =
run[A, S](NettyKyoServer())(v)

/** Runs a GraphQL server with a custom NettyKyoServer configuration.
*
* @param server
* The custom NettyKyoServer configuration
* @param v
* The HttpInterpreter to be used
* @param Frame
* Implicit Frame parameter
* @return
* A NettyKyoServerBinding wrapped in ZIOs and Abort effects
*/
def run[A, S](server: NettyKyoServer)(v: HttpInterpreter[Any, CalibanError] < (Resolvers & S))(
using Frame
): NettyKyoServerBinding < (ZIOs & Abort[CalibanError] & S) =
ZIOs.get(ZIO.runtime[Any]).map(runtime => run(server, runtime)(v))

/** Runs a GraphQL server with a custom Runner.
*
* @param runner
* The custom Runner to be used
* @param v
* The HttpInterpreter to be used
* @param tag
* Implicit Tag for Runner[R]
* @param frame
* Implicit Frame parameter
* @return
* A NettyKyoServerBinding wrapped in ZIOs and Abort effects
*/
def run[R, A, S](runner: Runner[R])(v: HttpInterpreter[Runner[R], CalibanError] < (Resolvers & S))(
using
tag: Tag[Runner[R]],
frame: Frame
): NettyKyoServerBinding < (ZIOs & Abort[CalibanError] & S) =
run[R, A, S](NettyKyoServer(), runner)(v)

/** Runs a GraphQL server with a custom NettyKyoServer configuration and Runner.
*
* @param server
* The custom NettyKyoServer configuration
* @param runner
* The custom Runner to be used
* @param v
* The HttpInterpreter to be used
* @param tag
* Implicit Tag for Runner[R]
* @param frame
* Implicit Frame parameter
* @return
* A NettyKyoServerBinding wrapped in ZIOs and Abort effects
*/
def run[R, A, S](server: NettyKyoServer, runner: Runner[R])(v: HttpInterpreter[Runner[R], CalibanError] < (Resolvers & S))(
using
tag: Tag[Runner[R]],
frame: Frame
): NettyKyoServerBinding < (ZIOs & Abort[CalibanError] & S) =
ZIOs.get(ZIO.runtime[Any]).map(runtime => run(server, runtime.withEnvironment(ZEnvironment(runner)))(v))

/** Runs a GraphQL server with a custom NettyKyoServer configuration and Runtime.
*
* @param server
* The custom NettyKyoServer configuration
* @param runtime
* The custom Runtime to be used
* @param v
* The HttpInterpreter to be used
* @param Frame
* Implicit Frame parameter
* @return
* A NettyKyoServerBinding wrapped in ZIOs and Abort effects
*/
def run[R, A, S](
server: NettyKyoServer,
runtime: Runtime[R]
Expand All @@ -60,6 +122,19 @@ object Resolvers:
bindings <- IO(server.addEndpoints(endpoints).start())
yield bindings

/** Creates an HttpInterpreter from a GraphQL API.
*
* @param api
* The GraphQL API to be interpreted
* @param requestCodec
* Implicit JsonCodec for GraphQLRequest
* @param responseValueCodec
* Implicit JsonCodec for ResponseValue
* @param Frame
* Implicit Frame parameter
* @return
* An HttpInterpreter wrapped in Resolvers effect
*/
def get[R](api: GraphQL[R])(using
requestCodec: JsonCodec[GraphQLRequest],
responseValueCodec: JsonCodec[ResponseValue]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,35 @@ import directInternal.*
import scala.annotation.tailrec
import scala.quoted.*

/** Defers the execution of a block of code, allowing the use of `await` within it.
*
* This macro transforms the given block of code to work with effectful computations, enabling the use of `await` to handle various types
* of effects. The `defer` block is desugared into regular monadic composition using `map`, making it equivalent to writing out the monadic
* code explicitly.
*
* @tparam A
* The type of the value returned by the deferred block
* @param f
* The block of code to be deferred
* @return
* A value of type `A < S`, where `S` represents the combined effects of all `await` calls
*/
transparent inline def defer[A](inline f: A) = ${ impl[A]('f) }

/** Awaits the result of an effectful computation.
*
* This function can only be used within a `defer` block. It suspends the execution of the current block until the result of the effectful
* computation is available. In the desugared monadic composition, `await` calls are transformed into appropriate `map` operations.
*
* @tparam A
* The type of the value being awaited
* @tparam S
* The type of the effect (e.g., IO, Abort, Choice, etc.)
* @param v
* The effectful computation to await
* @return
* The result of type A, once the computation completes
*/
inline def await[A, S](v: A < S): A =
compiletime.error("`await` must be used within a `defer` block")

Expand Down
Loading

0 comments on commit efd0976

Please sign in to comment.