diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/ApplicationCoroutineScope.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/ApplicationCoroutineScope.kt
index ff07849ae1c..036947f3b43 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/ApplicationCoroutineScope.kt
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/ApplicationCoroutineScope.kt
@@ -2,6 +2,8 @@ package org.jboss.resteasy.reactive.server.runtime.kotlin

 import io.vertx.core.Context
 import kotlinx.coroutines.*
+import org.jboss.resteasy.reactive.server.core.CurrentRequestManager
+import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
 import org.jboss.resteasy.reactive.spi.ThreadSetupAction
 import javax.annotation.PreDestroy
 import javax.inject.Singleton
@@ -27,14 +29,16 @@ class ApplicationCoroutineScope : CoroutineScope, AutoCloseable {
 /**
  * Dispatches the coroutine in Vertx IO thread.
  */
-class VertxDispatcher(private val vertxContext: Context, private val requestScope : ThreadSetupAction.ThreadState) : CoroutineDispatcher() {
+class VertxDispatcher(private val vertxContext: Context, private val requestScope : ThreadSetupAction.ThreadState, private val rrContext: ResteasyReactiveRequestContext) : CoroutineDispatcher() {
     override fun dispatch(context: CoroutineContext, block: Runnable) {
         // context propagation for suspending functions is not enabled yet, will be handled later
         vertxContext.runOnContext {
             requestScope.activate()
+            CurrentRequestManager.set(rrContext);
             try {
                 block.run()
             } finally {
+                CurrentRequestManager.set(null);
                 requestScope.deactivate()
             }
         }
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt
index 4cf40d8098d..97426b6a1ce 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt
@@ -27,7 +27,7 @@ class CoroutineInvocationHandler(private val invoker: EndpointInvoker,
         }

         val requestScope = requestContext.captureCDIRequestScope()
-        val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
+        val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope, requestContext)}
                 ?: throw IllegalStateException("No Vertx context found")

         logger.trace("Handling request with dispatcher {}", dispatcher)
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FilterUtils.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FilterUtils.kt
index 8ebd7e75820..07d8fe72e1d 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FilterUtils.kt
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FilterUtils.kt
@@ -7,7 +7,7 @@ import javax.enterprise.inject.spi.CDI

 fun prepareExecution(requestContext: ResteasyReactiveRequestContext): Pair<CoroutineDispatcher, ApplicationCoroutineScope> {
     val requestScope = requestContext.captureCDIRequestScope()
-    val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
+    val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope, requestContext)}
             ?: throw IllegalStateException("No Vertx context found")

     val coroutineScope = CDI.current().select(ApplicationCoroutineScope::class.java)
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt
index 6d32cb6e939..0dee691bac3 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/FlowToPublisherHandler.kt
@@ -19,7 +19,7 @@ class FlowToPublisherHandler : ServerRestHandler {
         if (result is Flow<*>) {

             val requestScope = requestContext.captureCDIRequestScope()
-            val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
+            val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope, requestContext)}
                     ?: throw IllegalStateException("No Vertx context found")

             val coroutineScope = CDI.current().select(ApplicationCoroutineScope::class.java)
diff --git a/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/GreetingResource.kt b/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/GreetingResource.kt
index cb3e6529c0d..b4ceaaa4fcb 100644
--- a/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/GreetingResource.kt
+++ b/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/GreetingResource.kt
@@ -1,14 +1,19 @@
 package io.quarkus.it.resteasy.reactive.kotlin

 import org.jboss.resteasy.reactive.RestHeader
+import javax.inject.Inject
 import javax.ws.rs.GET
 import javax.ws.rs.Path
+import javax.ws.rs.core.HttpHeaders

 @Path("/greeting")
-class GreetingResource {
+class GreetingResource(val headers: HttpHeaders) {

     @GET
-    suspend fun testSuspend(@RestHeader("firstName") firstName: String, @RestHeader("lastName") lastName: String) = Greeting("hello $firstName $lastName")
+    suspend fun testSuspend(@RestHeader("firstName") firstName: String): Greeting {
+        val lastName = headers.getHeaderString("lastName");
+        return Greeting("hello $firstName $lastName")
+    }
 }

 data class Greeting(val message:String)

RESTEasy Reactive and Kotlin coroutines throws "No RESTEasy Reactive request in progress"
### Describe the bug

When implementing a RESTEasy Reactive endpoint using Kotlin coroutines any attempt to access the current request throws IllegalStateException("No RESTEasy Reactive request in progress"). For example, any attempt at retrieving headers from an injected JAX-RS `HttpHeaders` interface throws the exception.

### Expected behavior

You are able to implement RESTEasy Reactive methods using Kotlin coroutines. That they work as expected and are able to access full range of API objects.

### Actual behavior

An IllegalStateException with "No RESTEasy Reactive request in progress" message is thrown.

### How to Reproduce?

[code-with-quarkus.zip](https://github.com/quarkusio/quarkus/files/7504012/code-with-quarkus.zip)

### Output of `uname -a` or `ver`

Darwin ... 21.1.0 Darwin Kernel Version 21.1.0: Wed Oct 13 17:33:24 PDT 2021; root:xnu-8019.41.5~1/RELEASE_ARM64_T8101 arm64

### Output of `java -version`

java version "17" 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

`2.4.1` and current HEAD of `main`

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d) Maven home: /Users/kdubb/.m2/wrapper/dists/apache-maven-3.8.1-bin/2l5mhf2pq2clrde7f7qp1rdt5m/apache-maven-3.8.1 Java version: 17, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home Default locale: en_US, platform encoding: UTF-8 OS name: "mac os x", version: "12.0.1", arch: "aarch64", family: "mac"

### Additional information

_No response_

package io.quarkus.smallrye.reactivemessaging

import kotlinx.coroutines.future.await
import org.eclipse.microprofile.reactive.messaging.Emitter

suspend fun <T> Emitter<T>.sendSuspending(t: T) = send(t).await()

package io.quarkus.smallrye.reactivemessaging

import kotlinx.coroutines.future.await
import org.eclipse.microprofile.reactive.messaging.Message

suspend fun <T> Message<T>.ackSuspending() = ack().await()

suspend fun <T> Message<T>.nackSuspending(t: Throwable) = nack(t).await()

package io.quarkus.mongodb.panache.kotlin

import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import io.quarkus.mongodb.panache.kotlin.runtime.KotlinMongoOperations.Companion.INSTANCE
import io.quarkus.panache.common.Parameters
import io.quarkus.panache.common.Sort
import io.quarkus.panache.common.impl.GenerateBridge
import org.bson.Document
import org.bson.types.ObjectId
import java.util.stream.Stream

/**
 * Define persistence and query methods for an Entity with a default ID type of [ObjectId]
 *
 * @param Entity the entity type
 */
interface PanacheMongoCompanion<Entity : PanacheMongoEntityBase>: PanacheMongoCompanionBase<Entity, ObjectId>

/**
 * Define persistence and query methods for an Entity with a type of Id
 *
 * @param Entity the entity type
 * @param Id the ID type
 */
interface PanacheMongoCompanionBase<Entity : PanacheMongoEntityBase, Id: Any> {
    /**
     * Find an entity of this type by ID.
     *
     * @param id the ID of the entity to find.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun findById(id: Id): Entity? = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, vararg params: Any?): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options with optional indexed parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, vararg params: Any?): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Map<String, Any?>): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Map<String, Any?>): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Parameters): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Parameters): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     *
     * @param query a query string
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document, sort: Document): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     *
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     *
     * @param sort the sort order to use
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(sort: Sort): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, vararg params: Any?): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, vararg params: Any?): List<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Map<String, Any?>): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Map<String, Any?>): List<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Parameters): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Parameters): List<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).list()`.
     *
     * @param query a query document
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).list()`.
     *
     * @param query a query document
     * @param sort the sort document
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document, sort: Document): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().list()`.
     *
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).list()`.
     *
     * @param sort the sort order to use
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(sort: Sort): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, vararg params: Any?): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, vararg params: Any?): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Map<String, Any?>): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Map<String, Any?>): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Parameters): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Parameters): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).stream()`.
     *
     * @param query a query Document
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: Document): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).stream()`.
     *
     * @param query a query Document
     * @param sort the sort strategy to use
     * @return a Stream containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: Document, sort: Document): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().stream()`.
     *
     * @return a Stream containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).stream()`.
     *
     * @param sort the sort order to use
     * @return a Stream containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(sort: Sort): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity in the database.
     *
     * @return the number of this type of entity in the database.
     * @see [count]
     */
    @GenerateBridge
    fun count(): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, vararg params: Any?): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Map<String, Any?>): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Parameters): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query
     *
     * @param query a query document
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: Document): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type from the database.
     *
     * @return the number of entities deleted.
     * @see [delete]
     */
    @GenerateBridge
    fun deleteAll(): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, vararg params: Any?): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Map<String, Any?>): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Parameters): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query
     *
     * @param query a query document
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: Document): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete an entity of this type by ID.
     *
     * @param id the ID of the entity to delete.
     * @return false if the entity was not deleted (not found).
     */
    @GenerateBridge
    fun deleteById(id: Id): Boolean = throw INSTANCE.implementationInjectionMissing()

    /**
     * Insert all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(entities: Iterable<Entity>) = INSTANCE.persist(entities)

    /**
     * Insert all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(entities: Stream<Entity>) = INSTANCE.persist(entities)

    /**
     * Insert all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(firstEntity: Entity, vararg entities: Entity) = INSTANCE.persist(firstEntity, *entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(entities: Iterable<Entity>) = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to insert
     * @see [update]
     */
    fun update(entities: Stream<Entity>) = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(firstEntity: Entity, vararg entities: Entity) = INSTANCE.update(firstEntity, *entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Iterable<Entity>) = INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Stream<Entity>) = INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(firstEntity: Entity, vararg entities: Entity) = INSTANCE.persistOrUpdate(firstEntity, *entities)

    /**
     * Update all entities of this type using the given update document with optional indexed parameters.
     * The returned [io.quarkus.mongodb.panache.common.PanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params optional sequence of indexed parameters
     * @return a new [io.quarkus.mongodb.panache.common.PanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, vararg params: Any?): io.quarkus.mongodb.panache.common.PanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.PanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     *
     * @param params map of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.PanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Map<String, Any?>): io.quarkus.mongodb.panache.common.PanacheUpdate =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.PanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     *
     * @param params [Parameters] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.PanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Parameters): io.quarkus.mongodb.panache.common.PanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Collection.
     *
     * @return the [MongoCollection] used by this entity
     */
    @GenerateBridge
    fun mongoCollection(): MongoCollection<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Database.
     *
     * @return the [MongoDatabase] used by this entity
     */
    @GenerateBridge
    fun mongoDatabase(): MongoDatabase = throw INSTANCE.implementationInjectionMissing()
}

package io.quarkus.mongodb.panache.kotlin

import org.bson.types.ObjectId

/**
 * Represents an entity with a generated ID field [id] of type [ObjectId]. If your
 * Mongo entities extend this class they gain the ID field and auto-generated accessors
 * to all their public fields, as well as all the useful methods from [PanacheMongoEntityBase].
 *
 * If you want a custom ID type or strategy, you can directly extend [PanacheMongoEntityBase]
 * instead, and write your own ID field. You will still get auto-generated accessors and
 * all the useful methods.
 *
 * @see PanacheMongoEntityBase
 */
abstract class PanacheMongoEntity : PanacheMongoEntityBase() {
    /**
     * The auto-generated ID field.
     * This field is set by Mongo when this entity is persisted.
     *
     * @see [PanacheMongoEntityBase.persist]
     */
    var id: ObjectId? = null

    /**
     * Default toString() implementation
     *
     * @return the class type and ID type
     */
    override fun toString(): String = "${this.javaClass.simpleName}<$id>"
}

package io.quarkus.mongodb.panache.kotlin

import io.quarkus.mongodb.panache.kotlin.runtime.KotlinMongoOperations.Companion.INSTANCE

/**
 * Represents an entity. If your Mongo entities extend this class they gain auto-generated accessors
 * to all their public fields, as well as a lot of useful methods. Unless you have a custom ID strategy, you
 * should not extend this class directly but extend [PanacheMongoEntity] instead.
 *
 * @see PanacheMongoEntity
 */
@Suppress("unused")
abstract class PanacheMongoEntityBase {

    /**
     * Persist this entity in the database.  This will set its ID field if not already set.
     *
     * @see [persist]
     */
    fun persist() {
        INSTANCE.persist(this)
    }

    /**
     * Update this entity in the database.
     *
     * @see [update]
     */
    fun update() {
        INSTANCE.update(this)
    }

    /**
     * Persist this entity in the database or update it if it already exists.
     *
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate() {
        INSTANCE.persistOrUpdate(this)
    }

    /**
     * Delete this entity from the database if it is already persisted.
     *
     * @see [delete]
     * @see [PanacheMongoCompanionBase.deleteAll]
     */
    fun delete() {
        INSTANCE.delete(this)
    }
}

package io.quarkus.mongodb.panache.kotlin

import org.bson.types.ObjectId

/**
 * Represents a Repository for a specific type of entity `Entity`, with an ID type
 * of `ObjectId`. Implementing this repository will gain you the exact same useful methods
 * that are on [PanacheMongoEntityBase]. If you have a custom ID strategy, you should
 * implement [PanacheMongoRepositoryBase] instead.
 *
 * @param Entity The type of entity to operate on
 */
interface PanacheMongoRepository<Entity: Any> : PanacheMongoRepositoryBase<Entity, ObjectId>

@file:Suppress("unused")

package io.quarkus.mongodb.panache.kotlin

import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import io.quarkus.panache.common.Parameters
import io.quarkus.panache.common.Sort
import io.quarkus.panache.common.impl.GenerateBridge
import org.bson.Document
import java.util.stream.Stream
import io.quarkus.mongodb.panache.kotlin.runtime.KotlinMongoOperations.Companion.INSTANCE

/**
 * Represents a Repository for a specific type of entity `Entity`, with an ID type
 * of `Id`. Implementing this repository will gain you the exact same useful methods
 * that are on [PanacheMongoEntityBase]. Unless you have a custom ID strategy, you should not
 * implement this interface directly but implement [PanacheMongoRepository] instead.
 *
 * @param Entity The type of entity to operate on
 * @param Id The ID type of the entity
 * @see PanacheMongoRepository<Id><Entity>
 */
interface PanacheMongoRepositoryBase<Entity: Any, Id: Any> {
    /**
     * Persist the given entity in the database.
     * This will set it's ID field if not already set.
     *
     * @param entity the entity to insert.
     * @see [persist]
     */
    fun persist(entity: Entity) = INSTANCE.persist(entity)

    /**
     * Update the given entity in the database.
     *
     * @param entity the entity to update.
     * @see [update]
     */
    fun update(entity: Entity) = INSTANCE.update(entity)

    /**
     * Persist the given entity in the database or update it if it already exist.
     *
     * @param entity the entity to update.
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entity: Entity) = INSTANCE.persistOrUpdate(entity)

    /**
     * Delete the given entity from the database, if it is already persisted.
     *
     * @param entity the entity to delete.
     * @see [delete]
     * @see [deleteAll]
     */
    fun delete(entity: Entity) = INSTANCE.delete(entity)

    /**
     * Find an entity of this type by ID.
     *
     * @param id the ID of the entity to find.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun findById(id: Id): Entity? = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, vararg params: Any?): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with optional indexed parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, vararg params: Any?): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Map<String, Any?>): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Map<String, Any?>): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Parameters): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Parameters): PanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     *
     * @param query a [Document] query
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a new [PanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document, sort: Document): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     *
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     *
     * @param sort the sort order to use
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(sort: Sort): PanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, vararg params: Any?): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, vararg params: Any?): List<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Map<String, Any?>): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Map<String, Any?>): List<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Parameters): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Parameters): List<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).list()`.
     *
     * @param query a [Document] query
     * @return a [List] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).list()`.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a [List] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document, sort: Document): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().list()`.
     *
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).list()`.
     *
     * @param sort the sort order to use
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(sort: Sort): List<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [Stream] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, vararg params: Any?): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [Stream] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, vararg params: Any?): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [Stream] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Map<String, Any?>): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [Stream] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Map<String, Any?>): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a [Stream] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Parameters): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a [Stream] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Parameters): Stream<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).stream()`.
     *
     * @param query a [Document] query
     * @return a [Stream] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun stream(query: Document): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).stream()`.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a [Stream] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun stream(query: Document, sort: Document): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().stream()`.
     *
     * @return a [Stream] containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(sort: Sort): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).stream()`.
     *
     * @return a [Stream] containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(): Stream<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity in the database.
     *
     * @return the number of this type of entity in the database.
     * @see [count]
     */
    @GenerateBridge
    fun count(): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, vararg params: Any?): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Map<String, Any?>): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Parameters): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query
     *
     * @param query a [Document] query
     * @return he number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: Document): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type from the database.
     *
     * @return the number of entities deleted.
     * @see [delete]
     */
    @GenerateBridge
    fun deleteAll(): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete an entity of this type by ID.
     *
     * @param id the ID of the entity to delete.
     * @return false if the entity was not deleted (not found).
     */
    @GenerateBridge
    fun deleteById(id: Id): Boolean = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, vararg params: Any?): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Map<String, Any?>): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Parameters): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query
     *
     * @param query a [Document] query
     * @return he number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun delete(query: Document): Long = throw INSTANCE.implementationInjectionMissing()

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(entities: Iterable<Entity>) = INSTANCE.persist(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(entities: Stream<Entity>) = INSTANCE.persist(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(firstEntity: Entity, vararg entities: Entity) = INSTANCE.persist(firstEntity, *entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(entities: Iterable<Entity>) = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(entities: Stream<Entity>) = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(firstEntity: Entity, vararg entities: Entity) = INSTANCE.update(firstEntity, *entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Iterable<Entity>) = INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Stream<Entity>) = INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun persistOrUpdate(firstEntity: Entity, vararg entities: Entity) =
            INSTANCE.persistOrUpdate(firstEntity, *entities)

    /**
     * Update all entities of this type by the given update document, with optional indexed parameters.
     * The returned [io.quarkus.mongodb.panache.common.PanacheUpdate] object will allow to restrict on which documents the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params optional sequence of indexed parameters
     * @return a new [io.quarkus.mongodb.panache.common.PanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, vararg params: Any?): io.quarkus.mongodb.panache.common.PanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.PanacheUpdate] object will allow to restrict on which documents the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params [Map] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.PanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Map<String, Any?>): io.quarkus.mongodb.panache.common.PanacheUpdate =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.PanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params [Parameters] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.PanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Parameters): io.quarkus.mongodb.panache.common.PanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Collection
     */
    @GenerateBridge
    fun mongoCollection(): MongoCollection<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Database.
     */
    @GenerateBridge
    fun mongoDatabase(): MongoDatabase = throw INSTANCE.implementationInjectionMissing()
}

package io.quarkus.mongodb.panache.kotlin

import com.mongodb.ReadPreference
import com.mongodb.client.model.Collation
import io.quarkus.panache.common.Page
import io.quarkus.panache.common.exception.PanacheQueryException
import java.util.stream.Stream

/**
 * Interface representing an entity query, which abstracts the use of paging, getting the number of results, and
 * operating on [List] or [Stream].
 *
 * Instances of this interface cannot mutate the query itself or its parameters: only paging information can be
 * modified, and instances of this interface can be reused to obtain multiple pages of results.
 *
 * @param Entity The entity type being queried
 */
interface PanacheQuery<Entity: Any> {
    /**
     * Defines a projection class: the getters, and the public fields, will be used to restrict which fields should be
     * retrieved from the database.
     *
     * @return a new query with the same state as the previous one (params, page, range, ...).
     */
    fun <NewEntity: Any> project(type: Class<NewEntity>): PanacheQuery<NewEntity>

    /**
     * Sets the current page.
     *
     * @param page the new page
     * @return this query, modified
     * @see .page
     * @see .page
     */
    fun page(page: Page): PanacheQuery<Entity>

    /**
     * Sets the current page.
     *
     * @param pageIndex the page index
     * @param pageSize the page size
     * @return this query, modified
     * @see .page
     * @see .page
     */
    fun page(pageIndex: Int, pageSize: Int): PanacheQuery<Entity>

    /**
     * Sets the current page to the next page
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .previousPage
     */
    fun nextPage(): PanacheQuery<Entity>

    /**
     * Sets the current page to the previous page (or the first page if there is no previous page)
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .nextPage
     */
    fun previousPage(): PanacheQuery<Entity>

    /**
     * Sets the current page to the first page
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .lastPage
     */
    fun firstPage(): PanacheQuery<Entity>

    /**
     * Sets the current page to the last page. This will cause reading of the entity count.
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .firstPage
     * @see .count
     */
    fun lastPage(): PanacheQuery<Entity>

    /**
     * Returns true if there is another page to read after the current one.
     * This will cause reading of the entity count.
     *
     * @return true if there is another page to read
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .hasPreviousPage
     * @see .count
     */
    fun hasNextPage(): Boolean

    /**
     * Returns true if there is a page to read before the current one.
     *
     * @return true if there is a previous page to read
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .hasNextPage
     */
    fun hasPreviousPage(): Boolean

    /**
     * Returns the total number of pages to be read using the current page size.
     * This will cause reading of the entity count.
     *
     * @return the total number of pages to be read using the current page size.
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     */
    fun pageCount(): Int

    /**
     * Returns the current page.
     *
     * @return the current page
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see .page
     * @see .page
     */
    fun page(): Page

    /**
     * Switch the query to use a fixed range (start index - last index) instead of a page.
     * As the range is fixed, subsequent pagination of the query is not possible.
     *
     * @param startIndex the index of the first element, starting at 0
     * @param lastIndex the index of the last element
     * @return this query, modified
     */
    fun range(startIndex: Int, lastIndex: Int): PanacheQuery<Entity>

    /**
     * Define the collation used for this query.
     *
     * @param collation the collation to be used for this query.
     * @return this query, modified
     */
    fun withCollation(collation: Collation): PanacheQuery<Entity>

    /**
     * Define the read preference used for this query.
     *
     * @param readPreference the read preference to be used for this query.
     * @return this query, modified
     */
    fun withReadPreference(readPreference: ReadPreference?): PanacheQuery<Entity>

    // Results
    /**
     * Reads and caches the total number of entities this query operates on. This causes a database
     * query with `SELECT COUNT(*)` and a query equivalent to the current query, minus
     * ordering.
     *
     * @return the total number of entities this query operates on, cached.
     */
    fun count(): Long

    /**
     * Returns the current page of results as a [List].
     *
     * @return the current page of results as a [List].
     * @see .stream
     * @see .page
     * @see .page
     */
    fun list(): List<Entity>

    /**
     * Returns the current page of results as a [Stream].
     *
     * @return the current page of results as a [Stream].
     * @see .list
     * @see .page
     * @see .page
     */
    fun stream(): Stream<Entity>

    /**
     * Returns the first result of the current page index. This ignores the current page size to fetch
     * a single result.
     *
     * @return the first result of the current page index, or null if there are no results.
     * @see .singleResult
     */
    fun firstResult(): Entity?

    /**
     * Executes this query for the current page and return a single result.
     *
     * @return the single result
     * @throws PanacheQueryException if there is not exactly one result.
     * @see .firstResult
     */
    fun singleResult(): Entity?
}

package io.quarkus.smallrye.reactivemessaging.runtime.kotlin

import io.quarkus.arc.Arc
import io.smallrye.reactive.messaging.Invoker
import io.vertx.core.Vertx
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import java.util.concurrent.CompletableFuture

abstract class AbstractSubscribingCoroutineInvoker(private val beanInstance: Any): Invoker {

    override fun invoke(vararg args: Any?): CompletableFuture<Any?> {
        val coroutineScope = Arc.container().instance(ApplicationCoroutineScope::class.java).get()
        val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let(::VertxDispatcher)
                ?: throw IllegalStateException("No Vertx context found")

        return coroutineScope.async(context = dispatcher) {
            invokeBean(beanInstance, args)
        }.asCompletableFuture()
    }

    abstract suspend fun invokeBean(beanInstance: Any, args: Array<out Any?>): Any?
}

package io.quarkus.smallrye.reactivemessaging.runtime.kotlin

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import javax.annotation.PreDestroy
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext

@Singleton
class ApplicationCoroutineScope : CoroutineScope, AutoCloseable {
    override val coroutineContext: CoroutineContext = SupervisorJob()

    @PreDestroy
    override fun close() {
        coroutineContext.cancel()
    }
}

package io.quarkus.smallrye.reactivemessaging.runtime.kotlin

import io.quarkus.arc.Arc
import io.vertx.core.Context
import kotlinx.coroutines.CoroutineDispatcher
import kotlin.coroutines.CoroutineContext

class VertxDispatcher(private val vertxContext: Context) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        val requestContext = Arc.container().requestContext()
        vertxContext.runOnContext {
            if (requestContext.isActive) {
                block.run()
            } else {
                try {
                    requestContext.activate()
                    block.run()
                } finally {
                    requestContext.terminate()
                }
            }
        }
    }
}

package org.acme

import javax.enterprise.context.ApplicationScoped

@ApplicationScoped
class MyHelloExample {

    fun hello(): String {
        return "My Example Hello Quarkus Codestart"
    }
}

package io.quarkus.hibernate.orm.panache.kotlin

import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.implementationInjectionMissing
import io.quarkus.hibernate.orm.panache.kotlin.runtime.KotlinJpaOperations.Companion.INSTANCE
import io.quarkus.panache.common.Parameters
import io.quarkus.panache.common.Sort
import io.quarkus.panache.common.impl.GenerateBridge
import java.util.stream.Stream
import javax.persistence.EntityManager
import javax.persistence.LockModeType

/**
 * Defines methods to be used via the companion objects of entities.
 *
 * @param Entity the entity type
 */
interface PanacheCompanion<Entity : PanacheEntityBase>: PanacheCompanionBase<Entity, Long>

/**
 * Defines methods to be used via the companion objects of entities.
 *
 * @param Entity the entity type
 */
interface PanacheCompanionBase<Entity : PanacheEntityBase, Id: Any> {

    /**
     * Returns the [EntityManager] for the <Entity> for extra operations (eg. CriteriaQueries)
     *
     * @return the [EntityManager] for the <Entity>
     */
    @GenerateBridge
    fun getEntityManager(): EntityManager = throw implementationInjectionMissing()

    /**
     * Find an entity of this type by ID.
     *
     * @param id the ID of the entity to find.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun findById(id: Id): Entity? = throw implementationInjectionMissing()

    /**
     * Find an entity of this type by ID and lock it.
     *
     * @param id the ID of the entity to find.
     * @param lockModeType the locking strategy to be used when retrieving the entity.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun findById(id: Id, lockModeType: LockModeType): Entity? = throw implementationInjectionMissing()

    /**
     * Find entities using a query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun find(query: String, vararg params: Any): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options with optional indexed parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, vararg params: Any): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun find(query: String, params: Map<String, Any>): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Map<String, Any>): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun find(query: String, params: Parameters): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Parameters): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type.
     *
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [PanacheCompanion.findAll]
     * @see [PanacheCompanion.listAll]
     * @see [PanacheCompanion.streamAll]
     */
    @GenerateBridge
    fun findAll(): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     *
     * @param sort the sort order to use
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [PanacheCompanion.findAll]
     * @see [PanacheCompanion.listAll]
     * @see [PanacheCompanion.streamAll]
     */
    @GenerateBridge
    fun findAll(sort: Sort): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun list(query: String, vararg params: Any): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, vararg params: Any): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun list(query: String, params: Map<String, Any>): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Map<String, Any>): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun list(query: String, params: Parameters): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.list]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Parameters): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().list()`.
     *
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.listAll]
     * @see [PanacheCompanion.findAll]
     * @see [PanacheCompanion.streamAll]
     */
    @GenerateBridge
    fun listAll(): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).list()`.
     *
     * @param sort the sort order to use
     * @return a [List] containing all results, without paging
     * @see [PanacheCompanion.listAll]
     * @see [PanacheCompanion.findAll]
     * @see [PanacheCompanion.streamAll]
     */
    @GenerateBridge
    fun listAll(sort: Sort): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.stream]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     */
    @GenerateBridge
    fun stream(query: String, vararg params: Any): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.stream]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, vararg params: Any): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.stream]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     */
    @GenerateBridge
    fun stream(query: String, params: Map<String, Any>): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.stream]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Map<String, Any>): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.stream]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     */
    @GenerateBridge
    fun stream(query: String, params: Parameters): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.stream]
     * @see [PanacheCompanion.find]
     * @see [PanacheCompanion.list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Parameters): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.streamAll]
     * @see [PanacheCompanion.findAll]
     * @see [PanacheCompanion.listAll]
     */
    @GenerateBridge
    fun streamAll(): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param sort the sort order to use
     * @return a Stream containing all results, without paging
     * @see [PanacheCompanion.streamAll]
     * @see [PanacheCompanion.findAll]
     * @see [PanacheCompanion.listAll]
     */
    @GenerateBridge
    fun streamAll(sort: Sort): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity in the database.
     *
     * @return the number of this type of entity in the database.
     * @see [PanacheCompanion.count]
     */
    @GenerateBridge
    fun count(): Long = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities counted.
     * @see [PanacheCompanion.count]
     */
    @GenerateBridge
    fun count(query: String, vararg params: Any): Long = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities counted.
     * @see [PanacheCompanion.count]
     */
    @GenerateBridge
    fun count(query: String, params: Map<String, Any>): Long = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities counted.
     * @see [PanacheCompanion.count]
     */
    @GenerateBridge
    fun count(query: String, params: Parameters): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type from the database.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @return the number of entities deleted.
     * @see [PanacheCompanion.delete]
     */
    @GenerateBridge
    fun deleteAll(): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with optional indexed parameters.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities deleted.
     * @see [PanacheCompanion.deleteAll]
     * @see [PanacheCompanion.delete]
     */
    @GenerateBridge
    fun delete(query: String, vararg params: Any): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities deleted.
     * @see [PanacheCompanion.deleteAll]
     * @see [PanacheCompanion.delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Map<String, Any>): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities deleted.
     * @see [PanacheCompanion.deleteAll]
     * @see [PanacheCompanion.delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Parameters): Long = throw implementationInjectionMissing()

    /**
     * Delete an entity of this type by ID.
     *
     * @param id the ID of the entity to delete.
     * @return false if the entity was not deleted (not found).
     */
    @GenerateBridge
    fun deleteById(id: Id): Boolean = throw implementationInjectionMissing()

    /**
     * Persist all given entities.
     *
     * @param entities the entities to persist
     * @see [PanacheCompanion.persist]
     */
    fun persist(entities: Iterable<Entity>) {
        INSTANCE.persist(entities)
    }

    /**
     * Persist all given entities.
     *
     * @param entities the entities to persist
     * @see [PanacheCompanion.persist]
     */
    fun persist(entities: Stream<Entity>) {
        INSTANCE.persist(entities)
    }

    /**
     * Persist all given entities.
     *
     * @param entities the entities to persist
     * @see [PanacheCompanion.persist]
     */
    fun persist(firstEntity: Entity, vararg entities: Entity) {
        INSTANCE.persist(firstEntity, *entities)
    }

    /**
     * Update all entities of this type matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities updated.
     * @see [PanacheCompanion.update]
     */
    @GenerateBridge
    fun update(query: String, vararg params: Any): Int = throw implementationInjectionMissing()

    /**
     * Update all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities updated.
     * @see [PanacheCompanion.update]
     */
    @GenerateBridge
    fun update(query: String, params: Map<String, Any>): Int = throw implementationInjectionMissing()

    /**
     * Update all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities updated.
     * @see [PanacheCompanion.update]
     */
    @GenerateBridge
    fun update(query: String, params: Parameters): Int = throw implementationInjectionMissing()

    /**
     * Flushes all pending changes to the database.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun flush() {
        throw implementationInjectionMissing()
    }
}

package io.quarkus.hibernate.orm.panache.kotlin

import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.MappedSuperclass

/**
 * Represents an entity with a generated ID field [id] of type [Long]. If your
 * Hibernate entities extend this class they gain the ID field and auto-generated accessors
 * to all their public fields (unless annotated with [Transient]), as well as all
 * the useful methods from [PanacheEntityBase].
 * <p>
 * If you want a custom ID type or strategy, you can directly extend [PanacheEntityBase]
 * instead, and write your own ID field. You will still get auto-generated accessors and
 * all the useful methods.
 *
 * @see PanacheEntityBase
 */
@MappedSuperclass
open class PanacheEntity: PanacheEntityBase {
    /**
     * The auto-generated ID field. This field is set by Hibernate ORM when this entity
     * is persisted.
     *
     * @see [PanacheEntity.persist]
     */
    @Id
    @GeneratedValue
    open var id: Long? = null

    /**
     * Default toString() implementation
     *
     * @return the class type and ID type
     */
    override fun toString() = "${javaClass.simpleName}<$id>"
}

package io.quarkus.hibernate.orm.panache.kotlin

import com.fasterxml.jackson.annotation.JsonIgnore
import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations
import io.quarkus.hibernate.orm.panache.kotlin.runtime.KotlinJpaOperations.Companion.INSTANCE
import javax.json.bind.annotation.JsonbTransient
import javax.persistence.EntityManager

/**
 * Represents an entity. If your Hibernate entities extend this class they gain auto-generated accessors
 * to all their public fields (unless annotated with [Transient]), as well as a lot of useful
 * methods. Unless you have a custom ID strategy, you should not extend this class directly but extend
 * [PanacheEntity] instead.
 *
 * @see PanacheEntity
 */
interface PanacheEntityBase {
    // Operations

    /**
     * Returns true if this entity is persistent in the database. If yes, all modifications to
     * its persistent fields will be automatically committed to the database at transaction
     * commit time.
     *
     * @return true if this entity is persistent in the database.
     */
    @JsonbTransient
    @JsonIgnore
    fun isPersistent(): Boolean = INSTANCE.isPersistent(this)

    /**
     * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set.
     *
     * @see PanacheEntityBase.isPersistent
     * @see PanacheEntityBase.flush
     * @see PanacheEntityBase.persistAndFlush
     */
    fun persist() {
        INSTANCE.persist(this)
    }

    /**
     * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set.
     * Then flushes all pending changes to the database.
     *
     * @see [PanacheEntityBase.isPersistent]
     * @see [PanacheEntityBase.flush]
     * @see [PanacheEntityBase.persist]
     */
    fun persistAndFlush() {
        INSTANCE.persist(this)
        INSTANCE.flush(this)
    }

    /**
     * Delete this entity from the database, if it is already persisted.
     *
     * @see [PanacheEntityBase.isPersistent]
     * @see [PanacheCompanion.deleteAll]
     */
    fun delete() {
        INSTANCE.delete(this)
    }
}

package io.quarkus.hibernate.orm.panache.kotlin

import io.quarkus.panache.common.Page
import io.quarkus.panache.common.Parameters
import org.hibernate.Session
import org.hibernate.annotations.Filter
import org.hibernate.annotations.FilterDef
import java.util.stream.Stream
import javax.persistence.LockModeType
import javax.persistence.NonUniqueResultException

/**
 * Interface representing an entity query, which abstracts the use of paging, getting the number of results, and
 * operating on [List] or java.util.Stream.
 *
 * Instances of this interface cannot mutate the query itself or its parameters: only paging information can be
 * modified, and instances of this interface can be reused to obtain multiple pages of results.
 *
 * @param Entity The entity type being queried
 */
interface PanacheQuery<Entity: Any> {
    /**
     * Defines a projection class: the getters, and the public fields, will be used to restrict which fields should be
     * retrieved from the database.
     *
     * @return a new query with the same state as the previous one (params, page, range, lockMode, hints, ...).
     */
    fun <NewEntity: Any> project(type: Class<NewEntity>): PanacheQuery<NewEntity>

    /**
     * Sets the current page.
     *
     * @param page the new page
     * @return this query, modified
     * @see [PanacheQuery.page]
     */
    fun page(page: Page): PanacheQuery<Entity>

    /**
     * Sets the current page.
     *
     * @param pageIndex the page index
     * @param pageSize the page size
     * @return this query, modified
     * @see [PanacheQuery.page]
     */
    fun page(pageIndex: Int, pageSize: Int): PanacheQuery<Entity>

    /**
     * Sets the current page to the next page
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.previousPage]
     */
    fun nextPage(): PanacheQuery<Entity>

    /**
     * Sets the current page to the previous page (or the first page if there is no previous page)
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.nextPage]
     */
    fun previousPage(): PanacheQuery<Entity>

    /**
     * Sets the current page to the first page
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.lastPage]
     */
    fun firstPage(): PanacheQuery<Entity>

    /**
     * Sets the current page to the last page. This will cause reading of the entity count.
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.firstPage]
     * @see [PanacheQuery.count]
     */
    fun lastPage(): PanacheQuery<Entity>

    /**
     * Returns true if there is another page to read after the current one.
     * This will cause reading of the entity count.
     *
     * @return true if there is another page to read
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.hasPreviousPage]
     * @see [PanacheQuery.count]
     */
    fun hasNextPage(): Boolean

    /**
     * Returns true if there is a page to read before the current one.
     *
     * @return true if there is a previous page to read
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.hasNextPage]
     */
    fun hasPreviousPage(): Boolean

    /**
     * Returns the total number of pages to be read using the current page size.
     * This will cause reading of the entity count.
     *
     * @return the total number of pages to be read using the current page size.
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     */
    fun pageCount(): Int

    /**
     * Returns the current page.
     *
     * @return the current page
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [PanacheQuery.page]
     */
    fun page(): Page

    /**
     * Switch the query to use a fixed range (start index - last index) instead of a page.
     * As the range is fixed, subsequent pagination of the query is not possible.
     *
     * @param startIndex the index of the first element, starting at 0
     * @param lastIndex the index of the last element
     * @return this query, modified
     */
    fun range(startIndex: Int, lastIndex: Int): PanacheQuery<Entity>

    /**
     * Define the locking strategy used for this query.
     *
     * @param lockModeType the locking strategy to be used for this query.
     * @return this query, modified
     */
    fun withLock(lockModeType: LockModeType): PanacheQuery<Entity>

    /**
     * Set a query property or hint on the underlying JPA Query.
     *
     * @param hintName name of the property or hint.
     * @param value value for the property or hint.
     * @return this query, modified
     */
    fun withHint(hintName: String, value: Any): PanacheQuery<Entity>

    /**
     *

     * Enables a Hibernate filter during fetching of results for this query. Your filter must be declared
     * with [FilterDef] on your entity or package, and enabled with [Filter] on your entity.
     *

     * WARNING: setting filters can only be done on the underlying Hibernate [Session] and so this
     * will modify the session's filters for the duration of obtaining the results (not while building
     * the query). Enabled filters will be removed from the session afterwards, but no effort is made to
     * preserve filters enabled on the session outside of this API.
     *
     * @param filterName The name of the filter to enable
     * @param parameters The set of parameters for the filter, if the filter requires parameters
     * @return this query, modified
     */
    fun filter(filterName: String, parameters: Parameters): PanacheQuery<Entity>

    /**
     *

     * Enables a Hibernate filter during fetching of results for this query. Your filter must be declared
     * with [FilterDef] on your entity or package, and enabled with [Filter] on your entity.
     *

     * WARNING: setting filters can only be done on the underlying Hibernate [Session] and so this
     * will modify the session's filters for the duration of obtaining the results (not while building
     * the query). Enabled filters will be removed from the session afterwards, but no effort is made to
     * preserve filters enabled on the session outside of this API.
     *
     * @param filterName The name of the filter to enable
     * @param parameters The set of parameters for the filter, if the filter requires parameters
     * @return this query, modified
     */
    fun filter(filterName: String, parameters: Map<String, Any>): PanacheQuery<Entity>

    /**
     *

     * Enables a Hibernate filter during fetching of results for this query. Your filter must be declared
     * with [FilterDef] on your entity or package, and enabled with [Filter] on your entity.
     *

     * WARNING: setting filters can only be done on the underlying Hibernate [Session] and so this
     * will modify the session's filters for the duration of obtaining the results (not while building
     * the query). Enabled filters will be removed from the session afterwards, but no effort is made to
     * preserve filters enabled on the session outside of this API.
     *
     * @param filterName The name of the filter to enable
     * @return this query, modified
     */
    fun filter(filterName: String): PanacheQuery<Entity>

    /**
     * Reads and caches the total number of entities this query operates on. This causes a database
     * query with SELECT COUNT(*) and a query equivalent to the current query, minus
     * ordering.
     *
     * @return the total number of entities this query operates on, cached.
     */
    fun count(): Long

    /**
     * Returns the current page of results as a [List].
     *
     * @return the current page of results as a [List].
     * @see [PanacheQuery.stream]
     * @see [PanacheQuery.page]
     */
    fun list(): List<Entity>

    /**
     * Returns the current page of results as a Stream.
     *
     * @return the current page of results as a Stream.
     * @see [PanacheQuery.list]
     * @see [PanacheQuery.page]
     */
    fun stream(): Stream<Entity>

    /**
     * Returns the first result of the current page index. This ignores the current page size to fetch
     * a single result.
     *
     * @return the first result of the current page index, or null if there are no results.
     * @see [PanacheQuery.singleResult]
     */
    fun firstResult(): Entity?

    /**
     * Executes this query for the current page and return a single result.
     *
     * @return the single result (throws if there is not exactly one)
     * @throws NoResultException if there is no result
     * @throws NonUniqueResultException if there are more than one result
     * @see [PanacheQuery.firstResult]
     */
    fun singleResult(): Entity
}

package io.quarkus.hibernate.orm.panache.kotlin

/**
 * Represents a Repository for a specific type of entity `Entity`, with an ID type of `Long`. Implementing this
 * interface will gain you the exact same useful methods that are on [PanacheEntity] and [PanacheCompanion]. If you
 * have a custom ID strategy, you should implement [PanacheRepositoryBase] instead.
 *
 * @param Entity The type of entity to operate on
 */
interface PanacheRepository<Entity : Any>: PanacheRepositoryBase<Entity, Long>

package io.quarkus.hibernate.orm.panache.kotlin

import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations
import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.implementationInjectionMissing
import io.quarkus.hibernate.orm.panache.kotlin.runtime.KotlinJpaOperations.Companion.INSTANCE
import io.quarkus.panache.common.Parameters
import io.quarkus.panache.common.Sort
import io.quarkus.panache.common.impl.GenerateBridge
import java.util.stream.Stream
import javax.persistence.EntityManager
import javax.persistence.LockModeType
import kotlin.reflect.KClass

/**
 * Represents a Repository for a specific type of entity `Entity`, with an ID type of `Id`. Implementing this interface
 * will gain you the exact same useful methods that are on [PanacheEntity] and [PanacheCompanion].
 *
 * @param Entity The type of entity to operate on
 * @param Id The ID type of the entity
 */
interface PanacheRepositoryBase<Entity : Any, Id: Any> {

    /**
     * Returns the [EntityManager] for the <Entity> for extra operations (eg. CriteriaQueries)
     *
     * @return the [EntityManager] for the <Entity>
     */
    @GenerateBridge
    fun getEntityManager(): EntityManager = throw implementationInjectionMissing()

    /**
     * Returns the [EntityManager] tied to the given class for extra operations (eg. CriteriaQueries)
     *
     * @return the [EntityManager] tied to the given class
     *
     * @deprecated use {@link Panache#getEntityManager(Class)} instead to access an entity manager for any entity class
     */
    @GenerateBridge
    @Deprecated(message="use Panache.getEntityManager(Class) instead to access an entity manager for any entity class")
    fun getEntityManager(clazz: KClass<Any>): EntityManager = throw implementationInjectionMissing()

    /**
     * Persist the given entity in the database, if not already persisted.
     *
     * @param entity the entity to persist.
     * @see [PanacheRepositoryBase.isPersistent]
     * @see [PanacheRepositoryBase.persist]
     */
    fun persist(entity: Entity) {
        INSTANCE.persist(entity)
    }

    /**
     * Persist the given entity in the database, if not already persisted.
     * Then flushes all pending changes to the database.
     *
     * @param entity the entity to persist.
     * @see [PanacheRepositoryBase.isPersistent]
     * @see [PanacheRepositoryBase.persist]
     */
    fun persistAndFlush(entity: Entity) {
        INSTANCE.persist(entity)
        INSTANCE.flush(entity)
    }

    /**
     * Delete the given entity from the database, if it is already persisted.
     *
     * @param entity the entity to delete.
     * @see [PanacheRepositoryBase.isPersistent]
     * @see [PanacheRepositoryBase.delete]
     * @see [PanacheRepositoryBase.deleteAll]
     */
    fun delete(entity: Entity) {
        INSTANCE.delete(entity)
    }

    /**
     * Returns true if the given entity is persistent in the database. If yes, all modifications to
     * its persistent fields will be automatically committed to the database at transaction
     * commit time.
     *
     * @param entity the entity to check
     * @return true if the entity is persistent in the database.
     */
    fun isPersistent(entity: Entity): Boolean = INSTANCE.isPersistent(entity)

    /**
     * Flushes all pending changes to the database using the default EntityManager.
     */
    fun flush() {
        getEntityManager().flush()
    }

    /**
     * Find an entity of this type by ID.
     *
     * @param id the ID of the entity to find.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun findById(id: Id): Entity? = throw implementationInjectionMissing()

    /**
     * Find an entity of this type by ID and lock it.
     *
     * @param id the ID of the entity to find.
     * @param lockModeType the locking strategy to be used when retrieving the entity.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge(targetReturnTypeErased = true)
    fun findById(id: Id, lockModeType: LockModeType): Entity? = throw implementationInjectionMissing()

    /**
     * Find entities using a query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun find(query: String, vararg params: Any): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with optional indexed parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, vararg params: Any): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun find(query: String, params: Map<String, Any>): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Map<String, Any>): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun find(query: String, params: Parameters): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a new [PanacheQuery] instance for the given query
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Parameters): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type.
     *
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [PanacheRepositoryBase.findAll]
     * @see [PanacheRepositoryBase.listAll]
     * @see [PanacheRepositoryBase.streamAll]
     */
    @GenerateBridge
    fun findAll(): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     *
     * @param sort the sort order to use
     * @return a new [PanacheQuery] instance to find all entities of this type.
     * @see [PanacheRepositoryBase.findAll]
     * @see [PanacheRepositoryBase.listAll]
     * @see [PanacheRepositoryBase.streamAll]
     */
    @GenerateBridge
    fun findAll(sort: Sort): PanacheQuery<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun list(query: String, vararg params: Any): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, vararg params: Any): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun list(query: String, params: Map<String, Any>): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Map<String, Any>): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun list(query: String, params: Parameters): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.list]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Parameters): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().list()`.
     *
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.listAll]
     * @see [PanacheRepositoryBase.findAll]
     * @see [PanacheRepositoryBase.streamAll]
     */
    @GenerateBridge
    fun listAll(): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).list()`.
     *
     * @param sort the sort order to use
     * @return a [List] containing all results, without paging
     * @see [PanacheRepositoryBase.listAll]
     * @see [PanacheRepositoryBase.findAll]
     * @see [PanacheRepositoryBase.streamAll]
     */
    @GenerateBridge
    fun listAll(sort: Sort): List<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.stream]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     */
    @GenerateBridge
    fun stream(query: String, vararg params: Any): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.stream]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, vararg params: Any): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.stream]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     */
    @GenerateBridge
    fun stream(query: String, params: Map<String, Any>): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.stream]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Map<String, Any>): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.stream]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     */
    @GenerateBridge
    fun stream(query: String, params: Parameters): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params Parameters of indexed parameters
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.stream]
     * @see [PanacheRepositoryBase.find]
     * @see [PanacheRepositoryBase.list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Parameters): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.streamAll]
     * @see [PanacheRepositoryBase.findAll]
     * @see [PanacheRepositoryBase.listAll]
     */
    @GenerateBridge
    fun streamAll(): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).stream()`.
     * It requires a transaction to work.
     * Without a transaction, the underlying cursor can be closed before the end of the stream.
     *
     * @param sort the sort order to use
     * @return a Stream containing all results, without paging
     * @see [PanacheRepositoryBase.streamAll]
     * @see [PanacheRepositoryBase.findAll]
     * @see [PanacheRepositoryBase.listAll]
     */
    @GenerateBridge
    fun streamAll(sort: Sort): Stream<Entity> = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity in the database.
     *
     * @return the number of this type of entity in the database.
     * @see [PanacheRepositoryBase.count]
     */
    @GenerateBridge
    fun count(): Long = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities counted.
     * @see [PanacheRepositoryBase.count]
     */
    @GenerateBridge
    fun count(query: String, vararg params: Any): Long = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities counted.
     * @see [PanacheRepositoryBase.count]
     */
    @GenerateBridge
    fun count(query: String, params: Map<String, Any>): Long = throw implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities counted.
     * @see [PanacheRepositoryBase.count]
     */
    @GenerateBridge
    fun count(query: String, params: Parameters): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type from the database.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @return the number of entities deleted.
     * @see [PanacheRepositoryBase.delete]
     */
    @GenerateBridge
    fun deleteAll(): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with optional indexed parameters.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities deleted.
     * @see [PanacheRepositoryBase.deleteAll]
     * @see [PanacheRepositoryBase.delete]
     */
    @GenerateBridge
    fun delete(query: String, vararg params: Any): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities deleted.
     * @see [PanacheRepositoryBase.deleteAll]
     * @see [PanacheRepositoryBase.delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Map<String, Any>): Long = throw implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * WARNING: the default implementation of this method uses a bulk delete query and ignores
     * cascading rules from the JPA model.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities deleted.
     * @see [PanacheRepositoryBase.deleteAll]
     * @see [PanacheRepositoryBase.delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Parameters): Long = throw implementationInjectionMissing()

    /**
     * Delete an entity of this type by ID.
     *
     * @param id the ID of the entity to delete.
     * @return false if the entity was not deleted (not found).
     */
    @GenerateBridge
    fun deleteById(id: Id): Boolean = throw implementationInjectionMissing()

    /**
     * Persist all given entities.
     *
     * @param entities the entities to persist
     * @see [PanacheRepositoryBase.persist]
     */
    fun persist(entities: Iterable<Entity>) {
        INSTANCE.persist(entities)
    }

    /**
     * Persist all given entities.
     *
     * @param entities the entities to persist
     * @see [PanacfluheRepositoryBase.persist]
     */
    fun persist(entities: Stream<Entity>) {
        INSTANCE.persist(entities)
    }

    /**
     * Persist all given entities.
     *
     * @param entities the entities to persist
     * @see [PanacheRepositoryBase.persist]
     */
    fun persist(firstEntity: Entity, vararg entities: Entity) {
        INSTANCE.persist(firstEntity, *entities)
    }

    /**
     * Update all entities of this type matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities updated.
     * @see [PanacheRepositoryBase.update]
     */
    @GenerateBridge
    fun update(query: String, vararg params: Any): Int = throw implementationInjectionMissing()

    /**
     * Update all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities updated.
     * @see [PanacheRepositoryBase.update]
     */
    @GenerateBridge
    fun update(query: String, params: Map<String, Any>): Int = throw implementationInjectionMissing()

    /**
     * Update all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params Parameters of named parameters
     * @return the number of entities updated.
     * @see [PanacheRepositoryBase.update]
     */
    @GenerateBridge
    fun update(query: String, params: Parameters): Int = throw implementationInjectionMissing()
}

package io.quarkus.mongodb.panache.kotlin.reactive

import io.quarkus.mongodb.panache.kotlin.reactive.runtime.KotlinReactiveMongoOperations.Companion.INSTANCE
import io.quarkus.mongodb.reactive.ReactiveMongoCollection
import io.quarkus.mongodb.reactive.ReactiveMongoDatabase
import io.quarkus.panache.common.Parameters
import io.quarkus.panache.common.Sort
import io.quarkus.panache.common.impl.GenerateBridge
import io.smallrye.mutiny.Multi
import io.smallrye.mutiny.Uni
import org.bson.Document
import org.bson.types.ObjectId
import java.util.stream.Stream

/**
 * Define persistence and query methods for an Entity with a default ID type of [ObjectId]
 *
 * @param Entity the entity type
 */
interface ReactivePanacheMongoCompanion<Entity : ReactivePanacheMongoEntityBase>
    : ReactivePanacheMongoCompanionBase<Entity, ObjectId>

/**
 * Define persistence and query methods for an Entity with a type of Id
 *
 * @param Entity the entity type
 * @param Id the ID type
 */
interface ReactivePanacheMongoCompanionBase<Entity : ReactivePanacheMongoEntityBase, Id: Any> {
    /**
     * Find an entity of this type by ID.
     *
     * @param id the ID of the entity to find.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge
    fun findById(id: Id): Uni<Entity?> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, vararg params: Any?): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with optional indexed parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, vararg params: Any?): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Map<String, Any?>): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Map<String, Any?>): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Parameters): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Parameters): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     *
     * @param query a [Document] query
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [list]
     * @see [stream]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [list]
     * @see [stream]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document, sort: Document): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     *
     * @return a new [ReactivePanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     *
     * @param sort the sort order to use
     * @return a new [ReactivePanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(sort: Sort): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [list]
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, vararg params: Any?): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [list]
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, vararg params: Any?): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [list]
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Map<String, Any?>): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [list]
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Map<String, Any?>): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [list]
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Parameters): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [list]
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Parameters): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).list()`.
     *
     * @param query a [Document] query
     * @return a [List] containing all results, without paging
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).list()`.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a [List] containing all results, without paging
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document, sort: Document): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().list()`.
     *
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).list()`.
     *
     * @param sort the sort order to use
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(sort: Sort): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [stream]
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, vararg params: Any?): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [stream]
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, vararg params: Any?): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [stream]
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Map<String, Any?>): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [stream]
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Map<String, Any?>): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [stream]
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Parameters): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [stream]
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Parameters): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).stream()`.
     *
     * @param query a [Document] query
     * @return a [Multi] containing all results, without paging
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun stream(query: Document): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).stream()`.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a [Multi] containing all results, without paging
     * @see [find]
     * @see [find]
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun stream(query: Document, sort: Document): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().stream()`.
     *
     * @return a [Multi] containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).stream()`.
     *
     * @param sort the sort order to use
     * @return a [Multi] containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(sort: Sort): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity in the database.
     *
     * @return the number of this type of entity in the database.
     * @see [count]
     * @see [count]
     * @see [count]
     */
    @GenerateBridge
    fun count(): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities counted.
     * @see [count]
     * @see [count]
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, vararg params: Any?): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities counted.
     * @see [count]
     * @see [count]
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Map<String, Any?>): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return the number of entities counted.
     * @see [count]
     * @see [count]
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Parameters): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query
     *
     * @param query a [Document] query
     * @return he number of entities counted.
     * @see [count]
     * @see [count]
     * @see [count]
     */
    @GenerateBridge
    fun count(query: Document): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type from the database.
     *
     * @return the number of entities deleted.
     * @see [delete]
     * @see [delete]
     * @see [delete]
     */
    @GenerateBridge
    fun deleteAll(): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete an entity of this type by ID.
     *
     * @param id the ID of the entity to delete.
     * @return false if the entity was not deleted (not found).
     */
    @GenerateBridge
    fun deleteById(id: Id): Uni<Boolean> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, vararg params: Any?): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Map<String, Any?>): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Parameters): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query
     *
     * @param query a [Document] query
     * @return he number of entities counted.
     * @see [count]
     * @see [count]
     * @see [count]
     */
    @GenerateBridge
    fun delete(query: Document): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Insert all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     * @see [persist]
     * @see [persist]
     */
    fun persist(entities: Iterable<Entity>): Uni<Void> = INSTANCE.persist(entities)

    /**
     * Insert all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     * @see [persist]
     * @see [persist]
     */
    fun persist(entities: Stream<Entity>): Uni<Void> = INSTANCE.persist(entities)

    /**
     * Insert all given entities.
     *
     * @param entities the entities to update
     * @see [persist]
     * @see [persist]
     * @see [persist]
     */
    fun persist(firstEntity: Entity, vararg entities: Entity): Uni<Void> = INSTANCE.persist(firstEntity, *entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     * @see [update]
     * @see [update]
     */
    fun update(entities: Iterable<Entity>): Uni<Void> = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to insert
     * @see [update]
     * @see [update]
     * @see [update]
     */
    fun update(entities: Stream<Entity>): Uni<Void> = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     * @see [update]
     * @see [update]
     */
    fun update(firstEntity: Entity, vararg entities: Entity): Uni<Void> = INSTANCE.update(firstEntity, *entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     * @see [persistOrUpdate]
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Iterable<Entity>): Uni<Void> = INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persistOrUpdate]
     * @see [persistOrUpdate]
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Stream<Entity>): Uni<Void> = INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     * @see [persistOrUpdate]
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(firstEntity: Entity, vararg entities: Entity): Uni<Void> =
            INSTANCE.persistOrUpdate(firstEntity, *entities)

    /**
     * Update all entities of this type by the given update document, with optional indexed parameters.
     * The returned [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params optional sequence of indexed parameters
     * @return a new [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] instance for the given update document
     * @see [update]
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, vararg params: Any?): io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params [Map] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Map<String, Any?>): io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params [Parameters] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Parameters): io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Collection.
     */
    @GenerateBridge
    fun mongoCollection(): ReactiveMongoCollection<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Database.
     */
    @GenerateBridge
    fun mongoDatabase(): ReactiveMongoDatabase = throw INSTANCE.implementationInjectionMissing()

}

package io.quarkus.mongodb.panache.kotlin.reactive

import org.bson.types.ObjectId

/**
 * Represents an entity with a generated ID field [id] of type [ObjectId]. If your
 * Mongo entities extend this class they gain the ID field and auto-generated accessors
 * to all their public fields, as well as all the useful methods from [ReactivePanacheMongoEntityBase].
 *
 * If you want a custom ID type or strategy, you can directly extend [ReactivePanacheMongoEntityBase]
 * instead, and write your own ID field. You will still get auto-generated accessors and
 * all the useful methods.
 *
 * @see ReactivePanacheMongoEntityBase
 */
abstract class ReactivePanacheMongoEntity : ReactivePanacheMongoEntityBase() {
    /**
     * The auto-generated ID field.
     * This field is set by Mongo when this entity is persisted.
     *
     * @see [persist]
     */
    var id: ObjectId? = null

    /**
     * Default toString() implementation
     *
     * @return the class type and ID type
     */
    override fun toString(): String = "${javaClass.simpleName}<$id>"
}

package io.quarkus.mongodb.panache.kotlin.reactive

import io.quarkus.mongodb.panache.kotlin.reactive.runtime.KotlinReactiveMongoOperations.Companion.INSTANCE
import io.smallrye.mutiny.Uni

/**
 * Represents an entity. If your Mongo entities extend this class they gain auto-generated accessors
 * to all their public fields, as well as a lot of useful
 * methods. Unless you have a custom ID strategy, you should not extend this class directly but extend
 * [ReactivePanacheMongoEntity] instead.
 *
 * @see [ReactivePanacheMongoEntity]
 */
abstract class ReactivePanacheMongoEntityBase {
    /**
     * Persist this entity in the database.
     * This will set it's ID field if not already set.
     *
     * @see [persist]
     */
    fun <T : ReactivePanacheMongoEntityBase> persist(): Uni<T> = INSTANCE.persist(this).map { this as T }

    /**
     * Update this entity in the database.
     *
     * @see [update]
     */
    fun <T : ReactivePanacheMongoEntityBase> update(): Uni<T> = INSTANCE.update(this).map { this as T }

    /**
     * Persist this entity in the database or update it if it already exist.
     *
     * @see [persistOrUpdate]
     */
    fun <T : ReactivePanacheMongoEntityBase> persistOrUpdate(): Uni<T> = INSTANCE.persistOrUpdate(this).map { this as T }

    /**
     * Delete this entity from the database, if it is already persisted.
     *
     * @see [delete]
     * @see [ReactivePanacheMongoCompanionBase.deleteAll]
     */
    fun delete(): Uni<Void> = INSTANCE.delete(this)
}

package io.quarkus.mongodb.panache.kotlin.reactive

import org.bson.types.ObjectId

/**
 * Represents a Repository for a specific type of entity `Entity`, with an ID type
 * of `ObjectId`. Implementing this repository will gain you the exact same useful methods
 * that are on [ReactivePanacheMongoEntityBase]. If you have a custom ID strategy, you should
 * implement [ReactivePanacheMongoRepositoryBase] instead.
 *
 * @param Entity The type of entity to operate on
 */
interface ReactivePanacheMongoRepository<Entity: Any> : ReactivePanacheMongoRepositoryBase<Entity, ObjectId>

@file:Suppress("unused")

package io.quarkus.mongodb.panache.kotlin.reactive

import io.quarkus.mongodb.panache.kotlin.reactive.runtime.KotlinReactiveMongoOperations.Companion.INSTANCE
import io.quarkus.mongodb.reactive.ReactiveMongoCollection
import io.quarkus.mongodb.reactive.ReactiveMongoDatabase
import io.quarkus.panache.common.Parameters
import io.quarkus.panache.common.Sort
import io.quarkus.panache.common.impl.GenerateBridge
import io.smallrye.mutiny.Multi
import io.smallrye.mutiny.Uni
import org.bson.Document
import java.util.stream.Stream

/**
 * Represents a Repository for a specific type of entity `Entity`, with an ID type
 * of `Id`. Implementing this repository will gain you the exact same useful methods
 * that are on [ReactivePanacheMongoEntityBase]. Unless you have a custom ID strategy, you should not
 * implement this interface directly but implement [ReactivePanacheMongoRepository] instead.
 *
 * @param Entity The type of entity to operate on
 * @param Id The ID type of the entity
 * @see [ReactivePanacheMongoRepository<Id><Entity>]
 */
interface ReactivePanacheMongoRepositoryBase<Entity: Any, Id: Any> {
    /**
     * Persist the given entity in the database.
     * This will set it's ID field if not already set.
     *
     * @param entity the entity to insert.
     * @see [persist]
     */
    fun persist(entity: Entity): Uni<Entity> = INSTANCE.persist(entity).map { entity }

    /**
     * Update the given entity in the database.
     *
     * @param entity the entity to update.
     * @see [update]
     */
    fun update(entity: Entity): Uni<Entity> = INSTANCE.update(entity).map { entity }

    /**
     * Persist the given entity in the database or update it if it already exist.
     *
     * @param entity the entity to update.
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entity: Entity): Uni<Entity> = INSTANCE.persistOrUpdate(entity).map { entity }

    /**
     * Delete the given entity from the database, if it is already persisted.
     *
     * @param entity the entity to delete.
     * @see [delete]
     * @see [deleteAll]
     */
    fun delete(entity: Entity): Uni<Void> = INSTANCE.delete(entity)

    /**
     * Find an entity of this type by ID.
     *
     * @param id the ID of the entity to find.
     * @return the entity found, or `null` if not found.
     */
    @GenerateBridge
    fun findById(id: Id): Uni<Entity?> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, vararg params: Any?): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with optional indexed parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, vararg params: Any?): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Map<String, Any?>): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Map<String, Any?>): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, params: Parameters): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a query and the given sort options, with named parameters.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: String, sort: Sort, params: Parameters): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     *
     * @param query a [Document] query
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a new [ReactivePanacheQuery] instance for the given query
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun find(query: Document, sort: Document): ReactivePanacheQuery<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     *
     * @return a new [ReactivePanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     *
     * @param sort the sort order to use
     * @return a new [ReactivePanacheQuery] instance to find all entities of this type.
     * @see [findAll]
     * @see [listAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun findAll(sort: Sort): ReactivePanacheQuery<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, vararg params: Any?): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, vararg params: Any?): Uni<List<Entity>> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Map<String, Any?>): Uni<List<Entity>> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Map<String, Any?>): Uni<List<Entity>> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).list()`.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, params: Parameters): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).list()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a [List] containing all results, without paging
     * @see [list]
     * @see [find]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: String, sort: Sort, params: Parameters): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).list()`.
     *
     * @param query a [Document] query
     * @return a [List] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).list()`.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a [List] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun list(query: Document, sort: Document): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().list()`.
     *
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).list()`.
     *
     * @param sort the sort order to use
     * @return a [List] containing all results, without paging
     * @see [listAll]
     * @see [findAll]
     * @see [streamAll]
     */
    @GenerateBridge
    fun listAll(sort: Sort): Uni<List<Entity>> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with optional indexed parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, vararg params: Any?): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with optional indexed parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params optional sequence of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, vararg params: Any?): Multi<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Map<String, Any?>): Multi<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Map] of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Map<String, Any?>): Multi<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query, with named parameters.
     * This method is a shortcut for `find(query, params).stream()`.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, params: Parameters): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities matching a query and the given sort options, with named parameters.
     * This method is a shortcut for `find(query, sort, params).stream()`.
     *
     * @param query a query string
     * @param sort the sort strategy to use
     * @param params [Parameters] of indexed parameters
     * @return a [Multi] containing all results, without paging
     * @see [stream]
     * @see [find]
     * @see [list]
     */
    @GenerateBridge
    fun stream(query: String, sort: Sort, params: Parameters): Multi<Entity> =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a BSON query.
     * This method is a shortcut for `find(query).stream()`.
     *
     * @param query a [Document] query
     * @return a [Multi] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun stream(query: Document): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find entities using a a BSON query and a BSON sort.
     * This method is a shortcut for `find(query, sort).stream()`.
     *
     * @param query a [Document] query
     * @param sort the [Document] sort
     * @return a [Multi] containing all results, without paging
     * @see [find]
     * @see [list]
     * @see [stream]
     */
    @GenerateBridge
    fun stream(query: Document, sort: Document): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type.
     * This method is a shortcut for `findAll().stream()`.
     *
     * @return a [Multi] containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(sort: Sort): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Find all entities of this type, in the given order.
     * This method is a shortcut for `findAll(sort).stream()`.
     *
     * @return a [Multi] containing all results, without paging
     * @see [streamAll]
     * @see [findAll]
     * @see [listAll]
     */
    @GenerateBridge
    fun streamAll(): Multi<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity in the database.
     *
     * @return the number of this type of entity in the database.
     * @see [count]
     */
    @GenerateBridge
    fun count(): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, vararg params: Any?): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Map<String, Any?>): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return the number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: String, params: Parameters): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Counts the number of this type of entity matching the given query
     *
     * @param query a [Document] query
     * @return he number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun count(query: Document): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type from the database.
     *
     * @return the number of entities deleted.
     * @see [delete]
     */
    @GenerateBridge
    fun deleteAll(): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete an entity of this type by ID.
     *
     * @param id the ID of the entity to delete.
     * @return false if the entity was not deleted (not found).
     */
    @GenerateBridge
    fun deleteById(id: Id): Uni<Boolean> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with optional indexed parameters.
     *
     * @param query a query string
     * @param params optional sequence of indexed parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, vararg params: Any?): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Map] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Map<String, Any?>): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query, with named parameters.
     *
     * @param query a query string
     * @param params [Parameters] of named parameters
     * @return the number of entities deleted.
     * @see [deleteAll]
     * @see [delete]
     */
    @GenerateBridge
    fun delete(query: String, params: Parameters): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Delete all entities of this type matching the given query
     *
     * @param query a [Document] query
     * @return he number of entities counted.
     * @see [count]
     */
    @GenerateBridge
    fun delete(query: Document): Uni<Long> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(entities: Iterable<Entity>): Uni<Void> = INSTANCE.persist(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(entities: Stream<Entity>): Uni<Void> = INSTANCE.persist(entities)

    /**
     * Persist all given entities.
     *
     * @param entities the entities to insert
     * @see [persist]
     */
    fun persist(firstEntity: Entity, vararg entities: Entity): Uni<Void> =
            INSTANCE.persist(firstEntity, *entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(entities: Iterable<Entity>): Uni<Void> = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(entities: Stream<Entity>): Uni<Void> = INSTANCE.update(entities)

    /**
     * Update all given entities.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun update(firstEntity: Entity, vararg entities: Entity): Uni<Void> =
            INSTANCE.update(firstEntity, *entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Iterable<Entity>): Uni<Void> =
            INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [persistOrUpdate]
     */
    fun persistOrUpdate(entities: Stream<Entity>): Uni<Void> =
            INSTANCE.persistOrUpdate(entities)

    /**
     * Persist all given entities or update them if they already exist.
     *
     * @param entities the entities to update
     * @see [update]
     */
    fun persistOrUpdate(firstEntity: Entity, vararg entities: Entity): Uni<Void> =
            INSTANCE.persistOrUpdate(firstEntity, *entities)

    /**
     * Update all entities of this type by the given update document, with optional indexed parameters.
     * The returned [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params optional sequence of indexed parameters
     * @return a new [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, vararg params: Any?): io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params [Map] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Map<String, Any?>): io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Update all entities of this type by the given update document, with named parameters.
     * The returned [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] object will allow to restrict on which document the update should be applied.
     *
     * @param update the update document, if it didn't contain any update operator, we add `$set`.
     * It can also be expressed as a query string.
     * @param params [Parameters] of named parameters
     * @return a new [io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate] instance for the given update document
     * @see [update]
     */
    @GenerateBridge
    fun update(update: String, params: Parameters): io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate =
            throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Collection
     */
    @GenerateBridge
    fun mongoCollection(): ReactiveMongoCollection<Entity> = throw INSTANCE.implementationInjectionMissing()

    /**
     * Allow to access the underlying Mongo Database.
     */
    @GenerateBridge
    fun mongoDatabase(): ReactiveMongoDatabase = throw INSTANCE.implementationInjectionMissing()
}

package io.quarkus.mongodb.panache.kotlin.reactive

import com.mongodb.ReadPreference
import com.mongodb.client.model.Collation
import io.quarkus.panache.common.Page
import io.smallrye.mutiny.Multi
import io.smallrye.mutiny.Uni

/**
 * Interface representing an entity query, which abstracts the use of paging, getting the number of results, and
 * operating on [List] or [Stream].
 *
 * Instances of this interface cannot mutate the query itself or its parameters: only paging information can be
 * modified, and instances of this interface can be reused to obtain multiple pages of results.
 *
 * @param Entity The entity type being queried
 */
interface ReactivePanacheQuery<Entity> {
    /**
     * Defines a projection class: the getters, and the public fields, will be used to restrict which fields should be
     * retrieved from the database.
     *
     * @return @return a new query with the same state as the previous one (params, page, range, ...).
     */
    fun <NewEntity> project(type: Class<NewEntity>): ReactivePanacheQuery<NewEntity>

    /**
     * Sets the current page.
     *
     * @param page the new page
     * @return this query, modified
     * @see [page]
     */
    fun page(page: Page): ReactivePanacheQuery<Entity>

    /**
     * Sets the current page.
     *
     * @param pageIndex the page index
     * @param pageSize the page size
     * @return this query, modified
     * @see [page]
     */
    fun page(pageIndex: Int, pageSize: Int): ReactivePanacheQuery<Entity>

    /**
     * Sets the current page to the next page
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [previousPage]
     */
    fun nextPage(): ReactivePanacheQuery<Entity>

    /**
     * Sets the current page to the previous page (or the first page if there is no previous page)
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [nextPage]
     */
    fun previousPage(): ReactivePanacheQuery<Entity>

    /**
     * Sets the current page to the first page
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [lastPage]
     */
    fun firstPage(): ReactivePanacheQuery<Entity>

    /**
     * Sets the current page to the last page. This will cause reading of the entity count.
     *
     * @return this query, modified
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [firstPage]
     * @see [count]
     */
    fun lastPage(): Uni<ReactivePanacheQuery<Entity>>

    /**
     * Returns true if there is another page to read after the current one.
     * This will cause reading of the entity count.
     *
     * @return true if there is another page to read
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [hasPreviousPage]
     * @see [count]
     */
    fun hasNextPage(): Uni<Boolean>

    /**
     * Returns true if there is a page to read before the current one.
     *
     * @return true if there is a previous page to read
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [hasNextPage]
     */
    fun hasPreviousPage(): Boolean

    /**
     * Returns the total number of pages to be read using the current page size.
     * This will cause reading of the entity count.
     *
     * @return the total number of pages to be read using the current page size.
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     */
    fun pageCount(): Uni<Int>

    /**
     * Returns the current page.
     *
     * @return the current page
     * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set
     * @see [page]
     */
    fun page(): Page

    /**
     * Switch the query to use a fixed range (start index - last index) instead of a page.
     * As the range is fixed, subsequent pagination of the query is not possible.
     *
     * @param startIndex the index of the first element, starting at 0
     * @param lastIndex the index of the last element
     * @return this query, modified
     */
    fun range(startIndex: Int, lastIndex: Int): ReactivePanacheQuery<Entity>

    /**
     * Define the collation used for this query.
     *
     * @param collation the collation to be used for this query.
     * @return this query, modified
     */
    fun withCollation(collation: Collation): ReactivePanacheQuery<Entity>

    /**
     * Define the read preference used for this query.
     *
     * @param readPreference the read preference to be used for this query.
     * @return this query, modified
     */
    fun withReadPreference(readPreference: ReadPreference): ReactivePanacheQuery<Entity>

    /**
     * Reads and caches the total number of entities this query operates on. This causes a database
     * query with `SELECT COUNT(*)` and a query equivalent to the current query, minus
     * ordering.
     *
     * @return the total number of entities this query operates on, cached.
     */
    fun count(): Uni<Long>

    /**
     * Returns the current page of results as a [List].
     *
     * @return the current page of results as a [List].
     * @see [page]
     */
    fun list(): Uni<List<Entity>>

    /**
     * Returns the current page of results as a [Stream].
     *
     * @return the current page of results as a [Stream].
     * @see [list]
     * @see [page]
     */
    fun stream(): Multi<Entity>

    /**
     * Returns the first result of the current page index. This ignores the current page size to fetch
     * a single result.
     *
     * @return the first result of the current page index, or null if there are no results.
     * @see [singleResult]
     */
    fun firstResult(): Uni<Entity?>

    /**
     * Executes this query for the current page and return a single result.
     *
     * @return the single result.
     * @throws io.quarkus.panache.common.exception.PanacheQueryException if there are more than one result.
     * @see [firstResult]
     */
    fun singleResult(): Uni<Entity?>
}

package io.quarkus.mongodb.panache.kotlin.runtime

import com.mongodb.client.ClientSession
import com.mongodb.client.MongoCollection
import io.quarkus.mongodb.panache.common.PanacheUpdate
import io.quarkus.mongodb.panache.kotlin.PanacheQuery
import io.quarkus.mongodb.panache.common.runtime.MongoOperations
import io.quarkus.mongodb.panache.common.runtime.PanacheUpdateImpl
import org.bson.Document
import java.util.stream.Stream

/**
 * Defines kotlin specific implementations of methods needed by [MongoOperations].
 */
class KotlinMongoOperations : MongoOperations<PanacheQuery<*>, PanacheUpdate>() {
    /**
     * Defines internal implementation details for use by quarkus.  Use of these members is highly discouraged as the
     * implementation may change without warning.
     */
    companion object {
        @JvmField
        val INSTANCE = KotlinMongoOperations()
    }

    /**
     * Creates the query implementation
     *
     * @param collection the collection to query
     * @param query the query to base the new query off of
     * @param sortDoc the sort document to use
     *
     * @return the new query implementation
     */
    override fun createQuery(collection: MongoCollection<*>, session: ClientSession?, query: Document?, sortDoc: Document?) =
            PanacheQueryImpl(collection, session, query, sortDoc)

    /**
     * Creates the update implementation
     *
     * @param collection the collection to query
     * @param entityClass the type to update
     * @param docUpdate the update document to start with
     *
     * @return the new query implementation
     */
    override fun createUpdate(collection: MongoCollection<*>, entityClass: Class<*>, docUpdate: Document) =
            PanacheUpdateImpl(this, entityClass, docUpdate, collection)

    /**
     * Extracts the query results in to a List.
     *
     * @param query the query to list
     *
     * @return a [List] of the results
     */
    override fun list(query: PanacheQuery<*>): List<*> = query.list()

    /**
     * Extracts the query results in to a Stream.
     *
     * @param query the query to stream
     *
     * @return a [Stream] of the results
     */
    override fun stream(query: PanacheQuery<*>): Stream<*> = query.stream()
}

package io.quarkus.mongodb.panache.kotlin.runtime

import com.mongodb.ReadPreference
import com.mongodb.client.ClientSession
import com.mongodb.client.MongoCollection
import com.mongodb.client.model.Collation
import io.quarkus.mongodb.panache.kotlin.PanacheQuery
import io.quarkus.mongodb.panache.common.runtime.CommonPanacheQueryImpl
import io.quarkus.panache.common.Page
import org.bson.conversions.Bson
import java.util.stream.Stream

class PanacheQueryImpl<Entity: Any> : PanacheQuery<Entity> {
    private val delegate: CommonPanacheQueryImpl<Entity>

    internal constructor(collection: MongoCollection<out Entity>?, session: ClientSession?, mongoQuery: Bson?, sort: Bson?) {
        delegate = CommonPanacheQueryImpl(collection, session, mongoQuery, sort)
    }

    private constructor(delegate: CommonPanacheQueryImpl<Entity>) {
        this.delegate = delegate
    }

    override fun <T: Any> project(type: Class<T>): PanacheQuery<T> {
        return PanacheQueryImpl(delegate.project(type))
    }

    override fun page(page: Page): PanacheQuery<Entity> {
        delegate.page<Entity>(page)
        return this
    }

    override fun page(pageIndex: Int, pageSize: Int): PanacheQuery<Entity> {
        return page(Page.of(pageIndex, pageSize))
    }

    override fun nextPage(): PanacheQuery<Entity> {
        delegate.nextPage<Entity>()
        return this
    }

    override fun previousPage(): PanacheQuery<Entity> {
        delegate.previousPage<Entity>()
        return this
    }

    override fun firstPage(): PanacheQuery<Entity> {
        delegate.firstPage<Entity>()
        return this
    }

    override fun lastPage(): PanacheQuery<Entity> {
        delegate.lastPage<Entity>()
        return this
    }

    override fun hasNextPage(): Boolean {
        return delegate.hasNextPage()
    }

    override fun hasPreviousPage(): Boolean {
        return delegate.hasPreviousPage()
    }

    override fun pageCount(): Int {
        return delegate.pageCount()
    }

    override fun page(): Page {
        return delegate.page()
    }

    override fun range(startIndex: Int, lastIndex: Int): PanacheQuery<Entity> {
        delegate.range<Entity>(startIndex, lastIndex)
        return this
    }

    override fun withCollation(collation: Collation): PanacheQuery<Entity> {
        delegate.withCollation<Entity>(collation)
        return this
    }

    override fun withReadPreference(readPreference: ReadPreference?): PanacheQuery<Entity> {
        delegate.withReadPreference<Entity>(readPreference)
        return this
    }

    // Results
    override fun count(): Long {
        return delegate.count()
    }

    override fun list(): List<Entity> {
        return delegate.list()
    }

    override fun stream(): Stream<Entity> {
        return delegate.stream()
    }

    override fun firstResult(): Entity? {
        return delegate.firstResult()
    }

    override fun singleResult(): Entity? {
        return delegate.singleResult()
    }
}

package io.quarkus.hibernate.orm.panache.kotlin.runtime

import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations
import javax.persistence.EntityManager

class KotlinJpaOperations : AbstractJpaOperations<PanacheQueryImpl<*>>() {
    override fun createPanacheQuery(em: EntityManager, query: String, orderBy: String?, paramsArrayOrMap: Any?) =
        PanacheQueryImpl<Any>(em, query, orderBy, paramsArrayOrMap)

    override fun list(query: PanacheQueryImpl<*>) = query.list()

    override fun stream(query: PanacheQueryImpl<*>) = query.stream()

    companion object {
        /**
         * Provides the default implementations for quarkus to wire up. Should not be used by third party developers.
         */
        @JvmField
        val INSTANCE = KotlinJpaOperations()
    }
}

package io.quarkus.hibernate.orm.panache.kotlin.runtime

import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations
import io.quarkus.runtime.annotations.Recorder

@Recorder
open class PanacheKotlinHibernateOrmRecorder {
    open fun setEntityToPersistenceUnit(entityToPersistenceUnit: Map<String?, String?>?) {
        AbstractJpaOperations.setEntityToPersistenceUnit(entityToPersistenceUnit)
    }
}

package io.quarkus.hibernate.orm.panache.kotlin.runtime

import io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl
import io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery
import io.quarkus.panache.common.Page
import io.quarkus.panache.common.Parameters
import java.util.stream.Stream
import javax.persistence.EntityManager
import javax.persistence.LockModeType

class PanacheQueryImpl<Entity: Any> : PanacheQuery<Entity> {
    private var delegate: CommonPanacheQueryImpl<Entity>

    internal constructor(em: EntityManager?, query: String?, orderBy: String?, paramsArrayOrMap: Any?) {
        delegate = CommonPanacheQueryImpl(em, query, orderBy, paramsArrayOrMap)
    }

    private constructor(delegate: CommonPanacheQueryImpl<Entity>) {
        this.delegate = delegate
    }

    // Builder
    override fun <NewEntity: Any> project(type: Class<NewEntity>): PanacheQuery<NewEntity> {
        return PanacheQueryImpl(delegate.project(type))
    }

    override fun page(page: Page): PanacheQuery<Entity> {
        delegate.page(page)
        return this
    }

    override fun page(pageIndex: Int, pageSize: Int): PanacheQuery<Entity> {
        delegate.page(pageIndex, pageSize)
        return this
    }

    override fun nextPage(): PanacheQuery<Entity> {
        delegate.nextPage()
        return this
    }

    override fun previousPage(): PanacheQuery<Entity> {
        delegate.previousPage()
        return this
    }

    override fun firstPage(): PanacheQuery<Entity> {
        delegate.firstPage()
        return this
    }

    override fun lastPage(): PanacheQuery<Entity> {
        delegate.lastPage()
        return this
    }

    override fun hasNextPage(): Boolean {
        return delegate.hasNextPage()
    }

    override fun hasPreviousPage(): Boolean {
        return delegate.hasPreviousPage()
    }

    override fun pageCount(): Int {
        return delegate.pageCount()
    }

    override fun page(): Page {
        return delegate.page()
    }

    override fun range(startIndex: Int, lastIndex: Int): PanacheQuery<Entity> {
        delegate.range(startIndex, lastIndex)
        return this
    }

    override fun withLock(lockModeType: LockModeType): PanacheQuery<Entity> {
        delegate.withLock(lockModeType)
        return this
    }

    override fun withHint(hintName: String, value: Any): PanacheQuery<Entity> {
        delegate.withHint(hintName, value)
        return this
    }

    override fun filter(filterName: String, parameters: Parameters): PanacheQuery<Entity> {
        delegate.filter(filterName, parameters.map())
        return this
    }

    override fun filter(filterName: String, parameters: Map<String, Any>): PanacheQuery<Entity> {
        delegate.filter(filterName, parameters)
        return this
    }

    override fun filter(filterName: String): PanacheQuery<Entity> {
        delegate.filter(filterName, emptyMap())
        return this
    }

    // Results
    override fun count(): Long {
        return delegate.count()
    }

    override fun list(): List<Entity> {
        return delegate.list()
    }

    override fun stream(): Stream<Entity> {
        return delegate.stream()
    }

    override fun firstResult(): Entity? {
        return delegate.firstResult()
    }

    override fun singleResult(): Entity {
        return delegate.singleResult()
    }
}

package io.quarkus.mongodb.panache.kotlin.reactive.runtime

import io.quarkus.mongodb.panache.common.reactive.ReactivePanacheUpdate
import io.quarkus.mongodb.panache.kotlin.reactive.ReactivePanacheQuery
import io.quarkus.mongodb.panache.common.reactive.runtime.ReactiveMongoOperations
import io.quarkus.mongodb.panache.common.reactive.runtime.ReactivePanacheUpdateImpl
import io.quarkus.mongodb.reactive.ReactiveMongoCollection
import io.smallrye.mutiny.Multi
import io.smallrye.mutiny.Uni
import org.bson.Document

/**
 * Defines kotlin specific implementations of methods needed by [ReactiveMongoOperations].
 */
class KotlinReactiveMongoOperations : ReactiveMongoOperations<ReactivePanacheQuery<*>, ReactivePanacheUpdate>() {
    /**
     * Defines internal implementation details for use by quarkus.  Use of these members is highly discouraged as the
     * implementation may change without warning.
     */
    companion object {
        @JvmField
        val INSTANCE = KotlinReactiveMongoOperations()
    }

    /**
     * Creates the query implementation
     *
     * @param collection the collection to query
     * @param query the query to base the new query off of
     * @param sortDoc the sort document to use
     *
     * @return the new query implementation
     */
    override fun createQuery(collection: ReactiveMongoCollection<*>, query: Document?, sortDoc: Document?) =
            ReactivePanacheQueryImpl(collection, query, sortDoc)

    /**
     * Creates the update implementation
     *
     * @param collection the collection to query
     * @param entityClass the type to update
     * @param docUpdate the update document to start with
     *
     * @return the new query implementation
     */
    override fun createUpdate(collection: ReactiveMongoCollection<*>, entityClass: Class<*>, docUpdate: Document) =
            ReactivePanacheUpdateImpl(this, entityClass, docUpdate, collection)

    /**
     * Extracts the query results in to a List.
     *
     * @param query the query to list
     *
     * @return the query results
     */
    override fun list(query: ReactivePanacheQuery<*>): Uni<List<*>> = query.list() as Uni<List<*>>

    /**
     * Extracts the query results in to a Stream.
     *
     * @param query the query to stream
     *
     * @return the query results
     */
    override fun stream(query: ReactivePanacheQuery<*>): Multi<*> = query.stream()
}

package io.quarkus.mongodb.panache.kotlin.reactive.runtime

import io.quarkus.mongodb.panache.common.reactive.runtime.CommonReactivePanacheQueryImpl
import io.quarkus.mongodb.reactive.ReactiveMongoCollection
import org.bson.conversions.Bson
import io.smallrye.mutiny.Uni
import com.mongodb.ReadPreference
import com.mongodb.client.model.Collation
import io.quarkus.mongodb.panache.kotlin.reactive.ReactivePanacheQuery
import io.quarkus.panache.common.Page
import io.smallrye.mutiny.Multi

class ReactivePanacheQueryImpl<Entity> : ReactivePanacheQuery<Entity> {
    private val delegate: CommonReactivePanacheQueryImpl<Entity>

    internal constructor(collection: ReactiveMongoCollection<out Entity>?, mongoQuery: Bson?, sort: Bson?) {
        delegate = CommonReactivePanacheQueryImpl(collection, mongoQuery, sort)
    }

    private constructor(delegate: CommonReactivePanacheQueryImpl<Entity>) {
        this.delegate = delegate
    }

    override fun <T> project(type: Class<T>): ReactivePanacheQuery<T> {
        return ReactivePanacheQueryImpl(delegate.project(type))
    }

    override fun page(page: Page): ReactivePanacheQuery<Entity> {
        delegate.page<Entity>(page)
        return this
    }

    override fun page(pageIndex: Int, pageSize: Int): ReactivePanacheQuery<Entity> {
        return page(Page.of(pageIndex, pageSize))
    }

    override fun nextPage(): ReactivePanacheQuery<Entity> {
        delegate.nextPage<Entity>()
        return this
    }

    override fun previousPage(): ReactivePanacheQuery<Entity> {
        delegate.previousPage<Entity>()
        return this
    }

    override fun firstPage(): ReactivePanacheQuery<Entity> {
        delegate.firstPage<Entity>()
        return this
    }

    override fun lastPage(): Uni<ReactivePanacheQuery<Entity>> {
        val uni: Uni<CommonReactivePanacheQueryImpl<Entity>> = delegate.lastPage()
        return uni.map { this }
    }

    override fun hasNextPage(): Uni<Boolean> {
        return delegate.hasNextPage()
    }

    override fun hasPreviousPage(): Boolean {
        return delegate.hasPreviousPage()
    }

    override fun pageCount(): Uni<Int> {
        return delegate.pageCount()
    }

    override fun page(): Page {
        return delegate.page()
    }

    override fun range(startIndex: Int, lastIndex: Int): ReactivePanacheQuery<Entity> {
        delegate.range<Entity>(startIndex, lastIndex)
        return this
    }

    override fun withCollation(collation: Collation): ReactivePanacheQuery<Entity> {
        delegate.withCollation<Entity>(collation)
        return this
    }

    override fun withReadPreference(readPreference: ReadPreference): ReactivePanacheQuery<Entity> {
        delegate.withReadPreference<Entity>(readPreference)
        return this
    }

    override fun count(): Uni<Long> {
        return delegate.count()
    }

    override fun list(): Uni<List<Entity>> {
        return delegate.list()
    }

    override fun stream(): Multi<Entity> {
        return delegate.stream()
    }

    override fun firstResult(): Uni<Entity?> {
        return delegate.firstResult()
    }

    override fun singleResult(): Uni<Entity?> {
        return delegate.singleResult()
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.smallrye.mutiny.Uni
import io.smallrye.mutiny.coroutines.asUni
import kotlinx.coroutines.async
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestFilter

/**
 * Base class used by Quarkus to generate an implementation at build-time that calls
 * a {@code suspend} method annotated with {@code @ServerRequestFilter}
 */
@Suppress("unused")
abstract class AbstractSuspendedRequestFilter : ResteasyReactiveContainerRequestFilter {

    abstract suspend fun doFilter(containerRequestContext: ResteasyReactiveContainerRequestContext): Any

    abstract fun handleResult(containerRequestContext: ResteasyReactiveContainerRequestContext, uniResult: Uni<*>)

    private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

    override fun filter(containerRequestContext: ResteasyReactiveContainerRequestContext) {
        val (dispatcher,coroutineScope) = prepareExecution(containerRequestContext.serverRequestContext as ResteasyReactiveRequestContext)

        val uni = coroutineScope.async(context = dispatcher) {
            // ensure the proper CL is not lost in dev-mode
            Thread.currentThread().contextClassLoader = originalTCCL

            // the implementation gets the proper values from the context and invokes the user supplied method
            doFilter(containerRequestContext)
        }.asUni()

        // the implementation should call the appropriate FilterUtil method
        handleResult(containerRequestContext, uni)
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import kotlinx.coroutines.launch
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerResponseFilter
import javax.ws.rs.container.ContainerResponseContext

/**
 * Base class used by Quarkus to generate an implementation at build-time that calls
 * a {@code suspend} method annotated with {@code @ServerResponseFilter}
 */
@Suppress("unused")
abstract class AbstractSuspendedResponseFilter : ResteasyReactiveContainerResponseFilter {

    abstract suspend fun doFilter(requestContext: ResteasyReactiveContainerRequestContext, responseContext: ContainerResponseContext): Any

    private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

    override fun filter(requestContext: ResteasyReactiveContainerRequestContext, responseContext: ContainerResponseContext) {
        val (dispatcher,coroutineScope) = prepareExecution(requestContext.serverRequestContext as ResteasyReactiveRequestContext)

        requestContext.suspend()
        coroutineScope.launch(context = dispatcher) {
            // ensure the proper CL is not lost in dev-mode
            Thread.currentThread().contextClassLoader = originalTCCL
            try {
                doFilter(requestContext, responseContext)
            } catch (t: Throwable) {
                (requestContext.serverRequestContext as ResteasyReactiveRequestContext).handleException(t, true)
            }
            requestContext.resume()
        }
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.vertx.core.Context
import kotlinx.coroutines.*
import org.jboss.resteasy.reactive.spi.ThreadSetupAction
import javax.annotation.PreDestroy
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext

/**
 * Application wide coroutine scope. Should start and die with the rest of the application, along with child coroutines
 * (structured concurrency).
 *
 * This should be the main interception point to place user specific [CoroutineContext.Element] objects. Things
 * like activity ids, logger context propagation, tracing, exception handlers, etc.
 */
@Singleton
class ApplicationCoroutineScope : CoroutineScope, AutoCloseable {
    override val coroutineContext: CoroutineContext = SupervisorJob()

    @PreDestroy
    override fun close() {
        coroutineContext.cancel()
    }
}

/**
 * Dispatches the coroutine in Vertx IO thread.
 */
class VertxDispatcher(private val vertxContext: Context, private val requestScope : ThreadSetupAction.ThreadState) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        // context propagation for suspending functions is not enabled yet, will be handled later
        vertxContext.runOnContext {
            requestScope.activate()
            try {
                block.run()
            } finally {
                requestScope.deactivate()
            }
        }
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import org.jboss.resteasy.reactive.server.spi.EndpointInvoker

/**
 * Base interface implemented by the synthetic beans that represent suspending rest endpoints.
 *
 * @see [EndpointInvoker]
 *
 * Also see {@code io.quarkus.resteasy.reactive.kotlin.deployment.KotlinCoroutineIntegrationProcessor} for the build-time part
 * of coroutine support
 */
interface CoroutineEndpointInvoker: EndpointInvoker {
    /**
     * Delegates control over the bean that defines the endpoint
     *
     * **API note:** Actual Java synthetic bytecode has a 3rd parameter that is the actual coroutine state machine.
     * @param instance the rest endpoint instance bean
     * @param parameters the call parameters
     * @return the coroutine result which may be the computation result, or a suspension point.
     */
    suspend fun invokeCoroutine(instance: Any, parameters: Array<out Any>): Any?
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.vertx.core.Vertx
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.EndpointInvoker
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler
import org.slf4j.LoggerFactory

private val logger = LoggerFactory.getLogger(CoroutineInvocationHandler::class.java)

class CoroutineInvocationHandler(private val invoker: EndpointInvoker,
                                 private val coroutineScope: CoroutineScope) : ServerRestHandler {

    private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

    override fun handle(requestContext: ResteasyReactiveRequestContext) {
        if (requestContext.result != null) {
            return
        }
        if (invoker !is CoroutineEndpointInvoker) {
            requestContext.handleException(IllegalStateException("Not a coroutine invoker"), true)
            return
        }

        val requestScope = requestContext.captureCDIRequestScope()
        val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
                ?: throw IllegalStateException("No Vertx context found")

        logger.trace("Handling request with dispatcher {}", dispatcher)

        requestContext.suspend()
        coroutineScope.launch(context = dispatcher) {
            // ensure the proper CL is not lost in dev-mode
            Thread.currentThread().contextClassLoader = originalTCCL
            try {
                requestContext.result = invoker.invokeCoroutine(requestContext.endpointInstance, requestContext.parameters)
            } catch (t: Throwable) {
                // passing true since the target doesn't change and we want response filters to be able to know what the resource method was
                requestContext.handleException(t, true)
            }
            requestContext.resume()
        }
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.quarkus.arc.Unremovable
import org.jboss.resteasy.reactive.server.spi.EndpointInvoker
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler
import javax.enterprise.context.ApplicationScoped
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Factory for the [CoroutineInvocationHandler] that is already part of the CDI container
 */
@Singleton
@Unremovable
class CoroutineInvocationHandlerFactory(private val applicationCoroutineScope: ApplicationCoroutineScope) {
    fun createHandler(invoker: EndpointInvoker): ServerRestHandler {
        return CoroutineInvocationHandler(invoker, applicationCoroutineScope)
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import org.jboss.resteasy.reactive.server.model.HandlerChainCustomizer
import org.jboss.resteasy.reactive.server.model.ServerResourceMethod
import org.jboss.resteasy.reactive.server.spi.EndpointInvoker
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler
import java.util.function.Supplier
import javax.enterprise.inject.spi.CDI

/**
 * Intercepts method invocations to force an EndpointInvoker.
 */
open class CoroutineMethodProcessor @Deprecated("Used only in synthetic code") constructor() : HandlerChainCustomizer {

    constructor(alternativeInvoker: Supplier<EndpointInvoker>): this() {
        this.alternativeInvoker = alternativeInvoker
    }

    lateinit var alternativeInvoker: Supplier<EndpointInvoker>

    // not pretty, but this seems to be a limitation of the current method scanning process
    // the HandlerChainCustomizer is called in a build step, but also at runtime to actually create the invocation handler
    // classes like SecurityContextOverrideHandler also use this approach
    private val handlerFactory by lazy { CDI.current().select(CoroutineInvocationHandlerFactory::class.java).get() }

    override fun alternateInvocationHandler(invoker: EndpointInvoker): ServerRestHandler {
        return handlerFactory.createHandler(invoker)
    }

    override fun alternateInvoker(method: ServerResourceMethod): Supplier<EndpointInvoker>? {
        return alternativeInvoker
    }
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.vertx.core.Vertx
import kotlinx.coroutines.CoroutineDispatcher
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import javax.enterprise.inject.spi.CDI

fun prepareExecution(requestContext: ResteasyReactiveRequestContext): Pair<CoroutineDispatcher, ApplicationCoroutineScope> {
    val requestScope = requestContext.captureCDIRequestScope()
    val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
            ?: throw IllegalStateException("No Vertx context found")

    val coroutineScope = CDI.current().select(ApplicationCoroutineScope::class.java)
    requestContext.suspend()

    return Pair(dispatcher, coroutineScope.get())
}

package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.smallrye.mutiny.coroutines.asMulti
import io.vertx.core.Vertx
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler
import java.util.concurrent.Executor
import javax.enterprise.inject.spi.CDI

class FlowToPublisherHandler : ServerRestHandler {

    private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

    override fun handle(requestContext: ResteasyReactiveRequestContext?) {
        val result = requestContext!!.result
        if (result is Flow<*>) {

            val requestScope = requestContext.captureCDIRequestScope()
            val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
                    ?: throw IllegalStateException("No Vertx context found")

            val coroutineScope = CDI.current().select(ApplicationCoroutineScope::class.java)
            requestContext.suspend()
            coroutineScope.get().launch(context = dispatcher) {
                // ensure the proper CL is not lost in dev-mode
                Thread.currentThread().contextClassLoader = originalTCCL
                requestContext.result = result.asMulti()
                //run in a direct invocation executor to run the rest of the invocation in the co-route scope
                //feels a bit fragile, but let's see how it goes
                requestContext.resume(Executor { it.run() })
            }
        }
    }
}

package org.acme

import io.smallrye.config.ConfigMapping
import io.smallrye.config.WithName

@ConfigMapping(prefix = "greeting")
interface GreetingConfig {

    @WithName("message")
    fun message(): String?

}

package org.acme

import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.Parameters

@Command(name = "greeting", mixinStandardHelpOptions = true)
class GreetingCommand : Runnable {

    @Parameters(paramLabel = "<name>", defaultValue = "picocli", description = ["Your name."])
    var name: String? = null
    override fun run() {
        System.out.printf("Hello %s, go go commando!\n", name)
    }

}

package org.acme

import io.quarkus.vertx.web.Route
import io.quarkus.vertx.web.RoutingExchange
import io.vertx.ext.web.RoutingContext
import javax.enterprise.context.ApplicationScoped

@ApplicationScoped
class MyDeclarativeRoutes {

    // neither path nor regex is set - match a path derived from the method name (ie helloRoute => /hello-route )
    @Route(methods = [Route.HttpMethod.GET])
    fun helloRoute(ex : RoutingExchange) {
       return ex.ok("Hello " + ex.getParam("name").orElse("Reactive Route") +" !!")
    }
}

package org.acme

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.QueryParam

@RegisterRestClient(baseUri = "https://stage.code.quarkus.io/api")
interface MyRemoteService {

    @GET
    @Path("/extensions")
    fun getExtensionsById(@QueryParam("id") id: String): Set<Extension>

    data class Extension(val id: String, val name: String, val shortName: String, val keywords: List<String>)
}

package org.acme

import io.quarkus.qute.Template
import io.quarkus.qute.TemplateInstance
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.MediaType

@Path("/some-page")
class SomePage(val page: Template) {

    @GET
    @Produces(MediaType.TEXT_HTML)
    operator fun get(@QueryParam("name") name: String?): TemplateInstance {
        return page.data("name", name)
    }
}

package org.acme

import org.eclipse.microprofile.health.HealthCheck
import org.eclipse.microprofile.health.HealthCheckResponse
import org.eclipse.microprofile.health.Liveness

@Liveness
public class MyLivenessCheck : HealthCheck {

    override fun call(): HealthCheckResponse {
        return HealthCheckResponse.up("alive")
    }

}

package ilove.quark.us

import java.io.IOException
import javax.enterprise.context.ApplicationScoped
import javax.websocket.*
import javax.websocket.server.PathParam
import javax.websocket.server.ServerEndpoint

@ServerEndpoint("/start-websocket/{name}")
@ApplicationScoped
class StartWebSocket {

    @OnOpen
    fun onOpen(session: Session?, @PathParam("name") name: String) {
        println("onOpen> $name")
    }

    @OnClose
    fun onClose(session: Session?, @PathParam("name") name: String) {
        println("onClose> $name")
    }

    @OnError
    fun onError(session: Session?, @PathParam("name") name: String, throwable: Throwable) {
        println("onError> $name: $throwable")
    }

    @OnMessage
    fun onMessage(message: String, @PathParam("name") name: String) {
        println("onMessage> $name: $message")
    }
}

package org.acme

import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType

@Path("{resource.path}")
class {resource.class-name} {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    fun hello() = "{resource.response}"
}

package org.acme

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("{resource.path}")
class {resource.class-name} {

    @GetMapping
    fun hello() = "{resource.response}"
}