mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-02 14:03:06 +00:00
Refactor RequestBroker to support context aware use - introduction of BrokerContext
This commit is contained in:
parent
3323325526
commit
07a35f8bd0
@ -203,6 +203,104 @@ suite "RequestBroker macro (async mode)":
|
||||
|
||||
DualResponse.clearProvider()
|
||||
|
||||
test "supports keyed providers (async, zero-arg)":
|
||||
SimpleResponse.clearProvider()
|
||||
|
||||
check SimpleResponse
|
||||
.setProvider(
|
||||
proc(): Future[Result[SimpleResponse, string]] {.async.} =
|
||||
ok(SimpleResponse(value: "default"))
|
||||
)
|
||||
.isOk()
|
||||
|
||||
check SimpleResponse
|
||||
.setProvider(
|
||||
BrokerContext(0x11111111'u32),
|
||||
proc(): Future[Result[SimpleResponse, string]] {.async.} =
|
||||
ok(SimpleResponse(value: "one")),
|
||||
)
|
||||
.isOk()
|
||||
|
||||
check SimpleResponse
|
||||
.setProvider(
|
||||
BrokerContext(0x22222222'u32),
|
||||
proc(): Future[Result[SimpleResponse, string]] {.async.} =
|
||||
ok(SimpleResponse(value: "two")),
|
||||
)
|
||||
.isOk()
|
||||
|
||||
let defaultRes = waitFor SimpleResponse.request()
|
||||
check defaultRes.isOk()
|
||||
check defaultRes.value.value == "default"
|
||||
|
||||
let res1 = waitFor SimpleResponse.request(BrokerContext(0x11111111'u32))
|
||||
check res1.isOk()
|
||||
check res1.value.value == "one"
|
||||
|
||||
let res2 = waitFor SimpleResponse.request(BrokerContext(0x22222222'u32))
|
||||
check res2.isOk()
|
||||
check res2.value.value == "two"
|
||||
|
||||
let missing = waitFor SimpleResponse.request(BrokerContext(0x33333333'u32))
|
||||
check missing.isErr()
|
||||
check missing.error.contains("no provider registered for broker context")
|
||||
|
||||
check SimpleResponse
|
||||
.setProvider(
|
||||
BrokerContext(0x11111111'u32),
|
||||
proc(): Future[Result[SimpleResponse, string]] {.async.} =
|
||||
ok(SimpleResponse(value: "dup")),
|
||||
)
|
||||
.isErr()
|
||||
|
||||
SimpleResponse.clearProvider()
|
||||
|
||||
test "supports keyed providers (async, with args)":
|
||||
KeyedResponse.clearProvider()
|
||||
|
||||
check KeyedResponse
|
||||
.setProvider(
|
||||
proc(key: string, subKey: int): Future[Result[KeyedResponse, string]] {.async.} =
|
||||
ok(KeyedResponse(key: "default-" & key, payload: $subKey))
|
||||
)
|
||||
.isOk()
|
||||
|
||||
check KeyedResponse
|
||||
.setProvider(
|
||||
BrokerContext(0xABCDEF01'u32),
|
||||
proc(key: string, subKey: int): Future[Result[KeyedResponse, string]] {.async.} =
|
||||
ok(KeyedResponse(key: "k1-" & key, payload: "p" & $subKey)),
|
||||
)
|
||||
.isOk()
|
||||
|
||||
check KeyedResponse
|
||||
.setProvider(
|
||||
BrokerContext(0xABCDEF02'u32),
|
||||
proc(key: string, subKey: int): Future[Result[KeyedResponse, string]] {.async.} =
|
||||
ok(KeyedResponse(key: "k2-" & key, payload: "q" & $subKey)),
|
||||
)
|
||||
.isOk()
|
||||
|
||||
let d = waitFor KeyedResponse.request("topic", 7)
|
||||
check d.isOk()
|
||||
check d.value.key == "default-topic"
|
||||
|
||||
let k1 = waitFor KeyedResponse.request(BrokerContext(0xABCDEF01'u32), "topic", 7)
|
||||
check k1.isOk()
|
||||
check k1.value.key == "k1-topic"
|
||||
check k1.value.payload == "p7"
|
||||
|
||||
let k2 = waitFor KeyedResponse.request(BrokerContext(0xABCDEF02'u32), "topic", 7)
|
||||
check k2.isOk()
|
||||
check k2.value.key == "k2-topic"
|
||||
check k2.value.payload == "q7"
|
||||
|
||||
let miss = waitFor KeyedResponse.request(BrokerContext(0xDEADBEEF'u32), "topic", 7)
|
||||
check miss.isErr()
|
||||
check miss.error.contains("no provider registered for broker context")
|
||||
|
||||
KeyedResponse.clearProvider()
|
||||
|
||||
## ---------------------------------------------------------------------------
|
||||
## Sync-mode brokers + tests
|
||||
## ---------------------------------------------------------------------------
|
||||
@ -370,6 +468,71 @@ suite "RequestBroker macro (sync mode)":
|
||||
|
||||
ImplicitResponseSync.clearProvider()
|
||||
|
||||
test "supports keyed providers (sync, zero-arg)":
|
||||
SimpleResponseSync.clearProvider()
|
||||
|
||||
check SimpleResponseSync
|
||||
.setProvider(
|
||||
proc(): Result[SimpleResponseSync, string] =
|
||||
ok(SimpleResponseSync(value: "default"))
|
||||
)
|
||||
.isOk()
|
||||
|
||||
check SimpleResponseSync
|
||||
.setProvider(
|
||||
BrokerContext(0x10101010'u32),
|
||||
proc(): Result[SimpleResponseSync, string] =
|
||||
ok(SimpleResponseSync(value: "ten")),
|
||||
)
|
||||
.isOk()
|
||||
|
||||
let defaultRes = SimpleResponseSync.request()
|
||||
check defaultRes.isOk()
|
||||
check defaultRes.value.value == "default"
|
||||
|
||||
let keyedRes = SimpleResponseSync.request(BrokerContext(0x10101010'u32))
|
||||
check keyedRes.isOk()
|
||||
check keyedRes.value.value == "ten"
|
||||
|
||||
let miss = SimpleResponseSync.request(BrokerContext(0x20202020'u32))
|
||||
check miss.isErr()
|
||||
check miss.error.contains("no provider registered for broker context")
|
||||
|
||||
SimpleResponseSync.clearProvider()
|
||||
|
||||
test "supports keyed providers (sync, with args)":
|
||||
KeyedResponseSync.clearProvider()
|
||||
|
||||
check KeyedResponseSync
|
||||
.setProvider(
|
||||
proc(key: string, subKey: int): Result[KeyedResponseSync, string] =
|
||||
ok(KeyedResponseSync(key: "default-" & key, payload: $subKey))
|
||||
)
|
||||
.isOk()
|
||||
|
||||
check KeyedResponseSync
|
||||
.setProvider(
|
||||
BrokerContext(0xA0A0A0A0'u32),
|
||||
proc(key: string, subKey: int): Result[KeyedResponseSync, string] =
|
||||
ok(KeyedResponseSync(key: "k-" & key, payload: "p" & $subKey)),
|
||||
)
|
||||
.isOk()
|
||||
|
||||
let d = KeyedResponseSync.request("topic", 2)
|
||||
check d.isOk()
|
||||
check d.value.key == "default-topic"
|
||||
|
||||
let keyed = KeyedResponseSync.request(BrokerContext(0xA0A0A0A0'u32), "topic", 2)
|
||||
check keyed.isOk()
|
||||
check keyed.value.key == "k-topic"
|
||||
check keyed.value.payload == "p2"
|
||||
|
||||
let miss = KeyedResponseSync.request(BrokerContext(0xB0B0B0B0'u32), "topic", 2)
|
||||
check miss.isErr()
|
||||
check miss.error.contains("no provider registered for broker context")
|
||||
|
||||
KeyedResponseSync.clearProvider()
|
||||
|
||||
## ---------------------------------------------------------------------------
|
||||
## POD / external type brokers + tests (distinct/alias behavior)
|
||||
## ---------------------------------------------------------------------------
|
||||
|
||||
26
waku/common/broker/broker_context.nim
Normal file
26
waku/common/broker/broker_context.nim
Normal file
@ -0,0 +1,26 @@
|
||||
import std/[strutils, sysrand]
|
||||
|
||||
type BrokerContext* = distinct uint32
|
||||
|
||||
func `==`*(a, b: BrokerContext): bool {.borrow.}
|
||||
|
||||
func `$`*(bc: BrokerContext): string =
|
||||
toHex(uint32(bc), 8)
|
||||
|
||||
const DefaultBrokerContext* = BrokerContext(0xCAFFE14E'u32)
|
||||
|
||||
proc NewBrokerContext*(): BrokerContext =
|
||||
## Generates a random non-default broker context (as a raw uint32).
|
||||
##
|
||||
## The default broker context is reserved for the provider at index 0.
|
||||
## This helper never returns that value.
|
||||
for _ in 0 ..< 16:
|
||||
let b = urandom(4)
|
||||
if b.len != 4:
|
||||
continue
|
||||
let key =
|
||||
(uint32(b[0]) shl 24) or (uint32(b[1]) shl 16) or (uint32(b[2]) shl 8) or
|
||||
uint32(b[3])
|
||||
if key != uint32(DefaultBrokerContext):
|
||||
return BrokerContext(key)
|
||||
BrokerContext(1'u32)
|
||||
@ -16,6 +16,18 @@
|
||||
## `async` mode is better to be used when you request date that may involve some long IO operation
|
||||
## or action.
|
||||
##
|
||||
## Default vs. context aware use:
|
||||
## Every generated broker is a thread-local global instance. This means each RequestBroker enables decoupled
|
||||
## data exchange threadwise. Sometimes we use brokers inside a context - like inside a component that has many modules or subsystems.
|
||||
## In case you would instantiate multiple such components in a single thread, and each component must has its own provider for the same RequestBroker type,
|
||||
## in order to avoid provider collision, you can use context aware RequestBroker.
|
||||
## Context awareness is supported through the `BrokerContext` argument for `setProvider`, `request`, `clearProvider` interfaces.
|
||||
## Suce use requires generating a new unique `BrokerContext` value per component instance, and spread it to all modules using the brokers.
|
||||
## Example, store the `BrokerContext` as a field inside the top level component instance, and spread around at initialization of the subcomponents..
|
||||
##
|
||||
## Default broker context is defined as `DefaultBrokerContext` constant. But if you don't need context awareness, you can use the
|
||||
## interfaces without context argument.
|
||||
##
|
||||
## Usage:
|
||||
## Declare your desired request type inside a `RequestBroker` macro, add any number of fields.
|
||||
## Define the provider signature, that is enforced at compile time.
|
||||
@ -89,7 +101,13 @@
|
||||
## After this, you can register a provider anywhere in your code with
|
||||
## `TypeName.setProvider(...)`, which returns error if already having a provider.
|
||||
## Providers are async procs/lambdas in default mode and sync procs in sync mode.
|
||||
## Only one provider can be registered at a time per signature type (zero arg and/or multi arg).
|
||||
##
|
||||
## Providers are stored as a broker-context keyed list:
|
||||
## - the default provider is always stored at index 0 (reserved broker context: 0)
|
||||
## - additional providers can be registered under arbitrary non-zero broker contexts
|
||||
##
|
||||
## The original `setProvider(handler)` / `request(...)` APIs continue to operate
|
||||
## on the default provider (broker context 0) for backward compatibility.
|
||||
##
|
||||
## Requests can be made from anywhere with no direct dependency on the provider by
|
||||
## calling `TypeName.request()` - with arguments respecting the signature(s).
|
||||
@ -139,11 +157,12 @@
|
||||
## automatically, so the caller only needs to provide the type definition.
|
||||
|
||||
import std/[macros, strutils]
|
||||
from std/sequtils import keepItIf
|
||||
import chronos
|
||||
import results
|
||||
import ./helper/broker_utils
|
||||
import ./helper/broker_utils, broker_context
|
||||
|
||||
export results, chronos
|
||||
export results, chronos, keepItIf, broker_context
|
||||
|
||||
proc errorFuture[T](message: string): Future[Result[T, string]] {.inline.} =
|
||||
## Build a future that is already completed with an error result.
|
||||
@ -329,11 +348,9 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
let typeNameLit = newLit(typeDisplayName)
|
||||
var zeroArgSig: NimNode = nil
|
||||
var zeroArgProviderName: NimNode = nil
|
||||
var zeroArgFieldName: NimNode = nil
|
||||
var argSig: NimNode = nil
|
||||
var argParams: seq[NimNode] = @[]
|
||||
var argProviderName: NimNode = nil
|
||||
var argFieldName: NimNode = nil
|
||||
|
||||
for stmt in body:
|
||||
case stmt.kind
|
||||
@ -368,7 +385,6 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
error("Only one zero-argument signature is allowed", stmt)
|
||||
zeroArgSig = stmt
|
||||
zeroArgProviderName = ident(sanitizeIdentName(typeIdent) & "ProviderNoArgs")
|
||||
zeroArgFieldName = ident("providerNoArgs")
|
||||
elif paramCount >= 1:
|
||||
if argSig != nil:
|
||||
error("Only one argument-based signature is allowed", stmt)
|
||||
@ -391,7 +407,6 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
error("Signature parameter must declare a name", paramDef)
|
||||
argParams.add(copyNimTree(paramDef))
|
||||
argProviderName = ident(sanitizeIdentName(typeIdent) & "ProviderWithArgs")
|
||||
argFieldName = ident("providerWithArgs")
|
||||
of nnkTypeSection, nnkEmpty:
|
||||
discard
|
||||
else:
|
||||
@ -400,7 +415,6 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
if zeroArgSig.isNil() and argSig.isNil():
|
||||
zeroArgSig = newEmptyNode()
|
||||
zeroArgProviderName = ident(sanitizeIdentName(typeIdent) & "ProviderNoArgs")
|
||||
zeroArgFieldName = ident("providerNoArgs")
|
||||
|
||||
var typeSection = newTree(nnkTypeSection)
|
||||
typeSection.add(newTree(nnkTypeDef, exportedTypeIdent, newEmptyNode(), objectDef))
|
||||
@ -423,12 +437,29 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
|
||||
var brokerRecList = newTree(nnkRecList)
|
||||
if not zeroArgSig.isNil():
|
||||
let zeroArgProvidersFieldName = ident("providersNoArgs")
|
||||
let zeroArgProvidersTupleTy = newTree(
|
||||
nnkTupleTy,
|
||||
newTree(nnkIdentDefs, ident("brokerCtx"), ident("BrokerContext"), newEmptyNode()),
|
||||
newTree(nnkIdentDefs, ident("handler"), zeroArgProviderName, newEmptyNode()),
|
||||
)
|
||||
let zeroArgProvidersSeqTy =
|
||||
newTree(nnkBracketExpr, ident("seq"), zeroArgProvidersTupleTy)
|
||||
brokerRecList.add(
|
||||
newTree(nnkIdentDefs, zeroArgFieldName, zeroArgProviderName, newEmptyNode())
|
||||
newTree(
|
||||
nnkIdentDefs, zeroArgProvidersFieldName, zeroArgProvidersSeqTy, newEmptyNode()
|
||||
)
|
||||
)
|
||||
if not argSig.isNil():
|
||||
let argProvidersFieldName = ident("providersWithArgs")
|
||||
let argProvidersTupleTy = newTree(
|
||||
nnkTupleTy,
|
||||
newTree(nnkIdentDefs, ident("brokerCtx"), ident("BrokerContext"), newEmptyNode()),
|
||||
newTree(nnkIdentDefs, ident("handler"), argProviderName, newEmptyNode()),
|
||||
)
|
||||
let argProvidersSeqTy = newTree(nnkBracketExpr, ident("seq"), argProvidersTupleTy)
|
||||
brokerRecList.add(
|
||||
newTree(nnkIdentDefs, argFieldName, argProviderName, newEmptyNode())
|
||||
newTree(nnkIdentDefs, argProvidersFieldName, argProvidersSeqTy, newEmptyNode())
|
||||
)
|
||||
let brokerTypeIdent = ident(sanitizeIdentName(typeIdent) & "Broker")
|
||||
let brokerTypeDef = newTree(
|
||||
@ -443,31 +474,97 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
|
||||
let globalVarIdent = ident("g" & sanitizeIdentName(typeIdent) & "Broker")
|
||||
let accessProcIdent = ident("access" & sanitizeIdentName(typeIdent) & "Broker")
|
||||
|
||||
var brokerNewBody = newStmtList()
|
||||
if not zeroArgSig.isNil():
|
||||
brokerNewBody.add(
|
||||
quote do:
|
||||
result.providersNoArgs =
|
||||
@[(brokerCtx: DefaultBrokerContext, handler: default(`zeroArgProviderName`))]
|
||||
)
|
||||
if not argSig.isNil():
|
||||
brokerNewBody.add(
|
||||
quote do:
|
||||
result.providersWithArgs =
|
||||
@[(brokerCtx: DefaultBrokerContext, handler: default(`argProviderName`))]
|
||||
)
|
||||
|
||||
var brokerInitChecks = newStmtList()
|
||||
if not zeroArgSig.isNil():
|
||||
brokerInitChecks.add(
|
||||
quote do:
|
||||
if `globalVarIdent`.providersNoArgs.len == 0:
|
||||
`globalVarIdent` = `brokerTypeIdent`.new()
|
||||
)
|
||||
if not argSig.isNil():
|
||||
brokerInitChecks.add(
|
||||
quote do:
|
||||
if `globalVarIdent`.providersWithArgs.len == 0:
|
||||
`globalVarIdent` = `brokerTypeIdent`.new()
|
||||
)
|
||||
|
||||
result.add(
|
||||
quote do:
|
||||
var `globalVarIdent` {.threadvar.}: `brokerTypeIdent`
|
||||
|
||||
proc new(_: type `brokerTypeIdent`): `brokerTypeIdent` =
|
||||
result = `brokerTypeIdent`()
|
||||
`brokerNewBody`
|
||||
|
||||
proc `accessProcIdent`(): var `brokerTypeIdent` =
|
||||
`brokerInitChecks`
|
||||
`globalVarIdent`
|
||||
|
||||
)
|
||||
|
||||
var clearBody = newStmtList()
|
||||
var clearBodyKeyed = newStmtList()
|
||||
let brokerCtxParamIdent = ident("brokerCtx")
|
||||
if not zeroArgSig.isNil():
|
||||
let zeroArgProvidersFieldName = ident("providersNoArgs")
|
||||
result.add(
|
||||
quote do:
|
||||
proc setProvider*(
|
||||
_: typedesc[`typeIdent`], handler: `zeroArgProviderName`
|
||||
): Result[void, string] =
|
||||
if not `accessProcIdent`().`zeroArgFieldName`.isNil():
|
||||
if not `accessProcIdent`().`zeroArgProvidersFieldName`[0].handler.isNil():
|
||||
return err("Zero-arg provider already set")
|
||||
`accessProcIdent`().`zeroArgFieldName` = handler
|
||||
`accessProcIdent`().`zeroArgProvidersFieldName`[0].handler = handler
|
||||
return ok()
|
||||
|
||||
)
|
||||
clearBody.add(
|
||||
|
||||
result.add(
|
||||
quote do:
|
||||
`accessProcIdent`().`zeroArgFieldName` = nil
|
||||
proc setProvider*(
|
||||
_: typedesc[`typeIdent`],
|
||||
brokerCtx: BrokerContext,
|
||||
handler: `zeroArgProviderName`,
|
||||
): Result[void, string] =
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
return setProvider(`typeIdent`, handler)
|
||||
|
||||
for entry in `accessProcIdent`().`zeroArgProvidersFieldName`:
|
||||
if entry.brokerCtx == brokerCtx:
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` &
|
||||
"): provider already set for broker context " & $brokerCtx
|
||||
)
|
||||
|
||||
`accessProcIdent`().`zeroArgProvidersFieldName`.add(
|
||||
(brokerCtx: brokerCtx, handler: handler)
|
||||
)
|
||||
return ok()
|
||||
|
||||
)
|
||||
clearBodyKeyed.add(
|
||||
quote do:
|
||||
if `brokerCtxParamIdent` == DefaultBrokerContext:
|
||||
`accessProcIdent`().`zeroArgProvidersFieldName`[0].handler =
|
||||
default(`zeroArgProviderName`)
|
||||
else:
|
||||
`accessProcIdent`().`zeroArgProvidersFieldName`.keepItIf(
|
||||
it.brokerCtx != `brokerCtxParamIdent`
|
||||
)
|
||||
)
|
||||
case mode
|
||||
of rbAsync:
|
||||
@ -476,11 +573,34 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
proc request*(
|
||||
_: typedesc[`typeIdent`]
|
||||
): Future[Result[`typeIdent`, string]] {.async: (raises: []).} =
|
||||
let provider = `accessProcIdent`().`zeroArgFieldName`
|
||||
return await request(`typeIdent`, DefaultBrokerContext)
|
||||
|
||||
)
|
||||
|
||||
result.add(
|
||||
quote do:
|
||||
proc request*(
|
||||
_: typedesc[`typeIdent`], brokerCtx: BrokerContext
|
||||
): Future[Result[`typeIdent`, string]] {.async: (raises: []).} =
|
||||
var provider: `zeroArgProviderName`
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
provider = `accessProcIdent`().`zeroArgProvidersFieldName`[0].handler
|
||||
else:
|
||||
for entry in `accessProcIdent`().`zeroArgProvidersFieldName`:
|
||||
if entry.brokerCtx == brokerCtx:
|
||||
provider = entry.handler
|
||||
break
|
||||
|
||||
if provider.isNil():
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` & "): no zero-arg provider registered"
|
||||
)
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` & "): no zero-arg provider registered"
|
||||
"RequestBroker(" & `typeNameLit` &
|
||||
"): no provider registered for broker context " & $brokerCtx
|
||||
)
|
||||
|
||||
let catchedRes = catch:
|
||||
await provider()
|
||||
|
||||
@ -507,10 +627,32 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
proc request*(
|
||||
_: typedesc[`typeIdent`]
|
||||
): Result[`typeIdent`, string] {.gcsafe, raises: [].} =
|
||||
let provider = `accessProcIdent`().`zeroArgFieldName`
|
||||
return request(`typeIdent`, DefaultBrokerContext)
|
||||
|
||||
)
|
||||
|
||||
result.add(
|
||||
quote do:
|
||||
proc request*(
|
||||
_: typedesc[`typeIdent`], brokerCtx: BrokerContext
|
||||
): Result[`typeIdent`, string] {.gcsafe, raises: [].} =
|
||||
var provider: `zeroArgProviderName`
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
provider = `accessProcIdent`().`zeroArgProvidersFieldName`[0].handler
|
||||
else:
|
||||
for entry in `accessProcIdent`().`zeroArgProvidersFieldName`:
|
||||
if entry.brokerCtx == brokerCtx:
|
||||
provider = entry.handler
|
||||
break
|
||||
|
||||
if provider.isNil():
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` & "): no zero-arg provider registered"
|
||||
)
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` & "): no zero-arg provider registered"
|
||||
"RequestBroker(" & `typeNameLit` &
|
||||
"): no provider registered for broker context " & $brokerCtx
|
||||
)
|
||||
|
||||
var providerRes: Result[`typeIdent`, string]
|
||||
@ -533,24 +675,54 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
|
||||
)
|
||||
if not argSig.isNil():
|
||||
let argProvidersFieldName = ident("providersWithArgs")
|
||||
result.add(
|
||||
quote do:
|
||||
proc setProvider*(
|
||||
_: typedesc[`typeIdent`], handler: `argProviderName`
|
||||
): Result[void, string] =
|
||||
if not `accessProcIdent`().`argFieldName`.isNil():
|
||||
if not `accessProcIdent`().`argProvidersFieldName`[0].handler.isNil():
|
||||
return err("Provider already set")
|
||||
`accessProcIdent`().`argFieldName` = handler
|
||||
`accessProcIdent`().`argProvidersFieldName`[0].handler = handler
|
||||
return ok()
|
||||
|
||||
)
|
||||
clearBody.add(
|
||||
|
||||
result.add(
|
||||
quote do:
|
||||
`accessProcIdent`().`argFieldName` = nil
|
||||
proc setProvider*(
|
||||
_: typedesc[`typeIdent`],
|
||||
brokerCtx: BrokerContext,
|
||||
handler: `argProviderName`,
|
||||
): Result[void, string] =
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
return setProvider(`typeIdent`, handler)
|
||||
|
||||
for entry in `accessProcIdent`().`argProvidersFieldName`:
|
||||
if entry.brokerCtx == brokerCtx:
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` &
|
||||
"): provider already set for broker context " & $brokerCtx
|
||||
)
|
||||
|
||||
`accessProcIdent`().`argProvidersFieldName`.add(
|
||||
(brokerCtx: brokerCtx, handler: handler)
|
||||
)
|
||||
return ok()
|
||||
|
||||
)
|
||||
clearBodyKeyed.add(
|
||||
quote do:
|
||||
if `brokerCtxParamIdent` == DefaultBrokerContext:
|
||||
`accessProcIdent`().`argProvidersFieldName`[0].handler =
|
||||
default(`argProviderName`)
|
||||
else:
|
||||
`accessProcIdent`().`argProvidersFieldName`.keepItIf(
|
||||
it.brokerCtx != `brokerCtxParamIdent`
|
||||
)
|
||||
)
|
||||
let requestParamDefs = cloneParams(argParams)
|
||||
let argNameIdents = collectParamNames(requestParamDefs)
|
||||
let providerSym = genSym(nskLet, "provider")
|
||||
var formalParams = newTree(nnkFormalParams)
|
||||
formalParams.add(copyNimTree(returnType))
|
||||
formalParams.add(
|
||||
@ -572,29 +744,96 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
of rbSync:
|
||||
quote:
|
||||
{.gcsafe, raises: [].}
|
||||
var providerCall = newCall(providerSym)
|
||||
|
||||
var forwardCall = newCall(ident("request"))
|
||||
forwardCall.add(copyNimTree(typeIdent))
|
||||
forwardCall.add(ident("DefaultBrokerContext"))
|
||||
for argName in argNameIdents:
|
||||
providerCall.add(argName)
|
||||
forwardCall.add(argName)
|
||||
|
||||
var requestBody = newStmtList()
|
||||
requestBody.add(
|
||||
quote do:
|
||||
let `providerSym` = `accessProcIdent`().`argFieldName`
|
||||
case mode
|
||||
of rbAsync:
|
||||
requestBody.add(
|
||||
quote do:
|
||||
return await `forwardCall`
|
||||
)
|
||||
of rbSync:
|
||||
requestBody.add(
|
||||
quote do:
|
||||
return `forwardCall`
|
||||
)
|
||||
|
||||
result.add(
|
||||
newTree(
|
||||
nnkProcDef,
|
||||
postfix(ident("request"), "*"),
|
||||
newEmptyNode(),
|
||||
newEmptyNode(),
|
||||
formalParams,
|
||||
requestPragmas,
|
||||
newEmptyNode(),
|
||||
requestBody,
|
||||
)
|
||||
)
|
||||
requestBody.add(
|
||||
|
||||
# Keyed request variant for the argument-based signature.
|
||||
let requestParamDefsKeyed = cloneParams(argParams)
|
||||
let argNameIdentsKeyed = collectParamNames(requestParamDefsKeyed)
|
||||
let providerSymKeyed = genSym(nskVar, "provider")
|
||||
var formalParamsKeyed = newTree(nnkFormalParams)
|
||||
formalParamsKeyed.add(copyNimTree(returnType))
|
||||
formalParamsKeyed.add(
|
||||
newTree(
|
||||
nnkIdentDefs,
|
||||
ident("_"),
|
||||
newTree(nnkBracketExpr, ident("typedesc"), copyNimTree(typeIdent)),
|
||||
newEmptyNode(),
|
||||
)
|
||||
)
|
||||
formalParamsKeyed.add(
|
||||
newTree(nnkIdentDefs, ident("brokerCtx"), ident("BrokerContext"), newEmptyNode())
|
||||
)
|
||||
for paramDef in requestParamDefsKeyed:
|
||||
formalParamsKeyed.add(paramDef)
|
||||
|
||||
let requestPragmasKeyed = requestPragmas
|
||||
var providerCallKeyed = newCall(providerSymKeyed)
|
||||
for argName in argNameIdentsKeyed:
|
||||
providerCallKeyed.add(argName)
|
||||
|
||||
var requestBodyKeyed = newStmtList()
|
||||
requestBodyKeyed.add(
|
||||
quote do:
|
||||
if `providerSym`.isNil():
|
||||
var `providerSymKeyed`: `argProviderName`
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
`providerSymKeyed` = `accessProcIdent`().`argProvidersFieldName`[0].handler
|
||||
else:
|
||||
for entry in `accessProcIdent`().`argProvidersFieldName`:
|
||||
if entry.brokerCtx == brokerCtx:
|
||||
`providerSymKeyed` = entry.handler
|
||||
break
|
||||
)
|
||||
requestBodyKeyed.add(
|
||||
quote do:
|
||||
if `providerSymKeyed`.isNil():
|
||||
if brokerCtx == DefaultBrokerContext:
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` &
|
||||
"): no provider registered for input signature"
|
||||
)
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` &
|
||||
"): no provider registered for input signature"
|
||||
"): no provider registered for broker context " & $brokerCtx
|
||||
)
|
||||
)
|
||||
|
||||
case mode
|
||||
of rbAsync:
|
||||
requestBody.add(
|
||||
requestBodyKeyed.add(
|
||||
quote do:
|
||||
let catchedRes = catch:
|
||||
await `providerCall`
|
||||
await `providerCallKeyed`
|
||||
if catchedRes.isErr():
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` & "): provider threw exception: " &
|
||||
@ -612,11 +851,11 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
return providerRes
|
||||
)
|
||||
of rbSync:
|
||||
requestBody.add(
|
||||
requestBodyKeyed.add(
|
||||
quote do:
|
||||
var providerRes: Result[`typeIdent`, string]
|
||||
try:
|
||||
providerRes = `providerCall`
|
||||
providerRes = `providerCallKeyed`
|
||||
except CatchableError as e:
|
||||
return err(
|
||||
"RequestBroker(" & `typeNameLit` & "): provider threw exception: " & e.msg
|
||||
@ -631,24 +870,52 @@ proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
|
||||
)
|
||||
return providerRes
|
||||
)
|
||||
# requestBody.add(providerCall)
|
||||
|
||||
result.add(
|
||||
newTree(
|
||||
nnkProcDef,
|
||||
postfix(ident("request"), "*"),
|
||||
newEmptyNode(),
|
||||
newEmptyNode(),
|
||||
formalParams,
|
||||
requestPragmas,
|
||||
formalParamsKeyed,
|
||||
requestPragmasKeyed,
|
||||
newEmptyNode(),
|
||||
requestBody,
|
||||
requestBodyKeyed,
|
||||
)
|
||||
)
|
||||
|
||||
block:
|
||||
var formalParamsClearKeyed = newTree(nnkFormalParams)
|
||||
formalParamsClearKeyed.add(newEmptyNode())
|
||||
formalParamsClearKeyed.add(
|
||||
newTree(
|
||||
nnkIdentDefs,
|
||||
ident("_"),
|
||||
newTree(nnkBracketExpr, ident("typedesc"), copyNimTree(typeIdent)),
|
||||
newEmptyNode(),
|
||||
)
|
||||
)
|
||||
formalParamsClearKeyed.add(
|
||||
newTree(nnkIdentDefs, brokerCtxParamIdent, ident("BrokerContext"), newEmptyNode())
|
||||
)
|
||||
|
||||
result.add(
|
||||
newTree(
|
||||
nnkProcDef,
|
||||
postfix(ident("clearProvider"), "*"),
|
||||
newEmptyNode(),
|
||||
newEmptyNode(),
|
||||
formalParamsClearKeyed,
|
||||
newEmptyNode(),
|
||||
newEmptyNode(),
|
||||
clearBodyKeyed,
|
||||
)
|
||||
)
|
||||
|
||||
result.add(
|
||||
quote do:
|
||||
proc clearProvider*(_: typedesc[`typeIdent`]) =
|
||||
`clearBody`
|
||||
clearProvider(`typeIdent`, DefaultBrokerContext)
|
||||
|
||||
)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user