Enhance MultiRequestBroker - similar to RequestBroker and EventBroker - with support for native and external types and context aware execution.

This commit is contained in:
NagyZoltanPeter 2025-12-17 08:37:53 +01:00
parent bc4ff10ea9
commit ca96391255
No known key found for this signature in database
GPG Key ID: 3E1F97CF4A7B6F42
2 changed files with 540 additions and 161 deletions

View File

@ -31,6 +31,23 @@ MultiRequestBroker:
suffix: string
): Future[Result[DualResponse, string]] {.async.}
type ExternalBaseType = string
MultiRequestBroker:
type NativeIntResponse = int
proc signatureFetch*(): Future[Result[NativeIntResponse, string]] {.async.}
MultiRequestBroker:
type ExternalAliasResponse = ExternalBaseType
proc signatureFetch*(): Future[Result[ExternalAliasResponse, string]] {.async.}
MultiRequestBroker:
type AlreadyDistinctResponse = distinct int
proc signatureFetch*(): Future[Result[AlreadyDistinctResponse, string]] {.async.}
suite "MultiRequestBroker":
test "aggregates zero-argument providers":
discard NoArgResponse.setProvider(
@ -211,6 +228,99 @@ suite "MultiRequestBroker":
test "ref providers returning nil fail request":
DualResponse.clearProviders()
test "supports native request types":
NativeIntResponse.clearProviders()
discard NativeIntResponse.setProvider(
proc(): Future[Result[NativeIntResponse, string]] {.async.} =
ok(NativeIntResponse(1))
)
discard NativeIntResponse.setProvider(
proc(): Future[Result[NativeIntResponse, string]] {.async.} =
ok(NativeIntResponse(2))
)
let res = waitFor NativeIntResponse.request()
check res.isOk()
check res.get().len == 2
check res.get().anyIt(int(it) == 1)
check res.get().anyIt(int(it) == 2)
NativeIntResponse.clearProviders()
test "supports external request types":
ExternalAliasResponse.clearProviders()
discard ExternalAliasResponse.setProvider(
proc(): Future[Result[ExternalAliasResponse, string]] {.async.} =
ok(ExternalAliasResponse("hello"))
)
let res = waitFor ExternalAliasResponse.request()
check res.isOk()
check res.get().len == 1
check ExternalBaseType(res.get()[0]) == "hello"
ExternalAliasResponse.clearProviders()
test "supports already-distinct request types":
AlreadyDistinctResponse.clearProviders()
discard AlreadyDistinctResponse.setProvider(
proc(): Future[Result[AlreadyDistinctResponse, string]] {.async.} =
ok(AlreadyDistinctResponse(7))
)
let res = waitFor AlreadyDistinctResponse.request()
check res.isOk()
check res.get().len == 1
check int(res.get()[0]) == 7
AlreadyDistinctResponse.clearProviders()
test "context-aware providers are isolated":
NoArgResponse.clearProviders()
let ctxA = NewBrokerContext()
let ctxB = NewBrokerContext()
discard NoArgResponse.setProvider(
ctxA,
proc(): Future[Result[NoArgResponse, string]] {.async.} =
ok(NoArgResponse(label: "a")),
)
discard NoArgResponse.setProvider(
ctxB,
proc(): Future[Result[NoArgResponse, string]] {.async.} =
ok(NoArgResponse(label: "b")),
)
let resA = waitFor NoArgResponse.request(ctxA)
check resA.isOk()
check resA.get().len == 1
check resA.get()[0].label == "a"
let resB = waitFor NoArgResponse.request(ctxB)
check resB.isOk()
check resB.get().len == 1
check resB.get()[0].label == "b"
let resDefault = waitFor NoArgResponse.request()
check resDefault.isOk()
check resDefault.get().len == 0
NoArgResponse.clearProviders(ctxA)
let clearedA = waitFor NoArgResponse.request(ctxA)
check clearedA.isOk()
check clearedA.get().len == 0
let stillB = waitFor NoArgResponse.request(ctxB)
check stillB.isOk()
check stillB.get().len == 1
check stillB.get()[0].label == "b"
NoArgResponse.clearProviders(ctxB)
discard DualResponse.setProvider(
proc(): Future[Result[DualResponse, string]] {.async.} =
let nilResponse: DualResponse = nil

View File

@ -5,12 +5,35 @@
## need for direct dependencies in between.
## Worth considering using it for use cases where you need to collect data from multiple providers.
##
## Provides a declarative way to define an immutable value type together with a
## thread-local broker that can register multiple asynchronous providers, dispatch
## typed requests, and clear handlers. Unlike `RequestBroker`,
## every call to `request` fan-outs to every registered provider and returns with
## collected responses.
## Request succeeds if all providers succeed, otherwise fails with an error.
## Generates a standalone, type-safe request broker for the declared type.
## The macro exports the value type itself plus a broker companion that manages
## providers via thread-local storage.
##
## Unlike `RequestBroker`, every call to `request` fan-outs to every registered
## provider and returns all collected responses.
## The request succeeds only if all providers succeed, otherwise it fails.
##
## Type definitions:
## - Inline `object` / `ref object` definitions are supported.
## - Native types, aliases, and externally-defined types are also supported.
## In that case, MultiRequestBroker will automatically wrap the declared RHS
## type in `distinct` unless you already used `distinct`.
## This keeps request types unique even when multiple brokers share the same
## underlying base type.
##
## Default vs. context aware use:
## Every generated broker is a thread-local global instance.
## Sometimes you want multiple independent provider sets for the same request
## type within the same thread (e.g. multiple components). For that, you can use
## context-aware MultiRequestBroker.
##
## Context awareness is supported through the `BrokerContext` argument for
## `setProvider`, `request`, `removeProvider`, and `clearProviders`.
## Provider stores are kept separate per broker context.
##
## Default broker context is defined as `DefaultBrokerContext`. If you don't
## need context awareness, you can keep using the interfaces without the context
## argument, which operate on `DefaultBrokerContext`.
##
## Usage:
##
@ -29,14 +52,17 @@
##
## ```
##
## You regiser request processor (proveder) at any place of the code without the need to know of who ever may request.
## Respectively to the defined signatures register provider functions with `TypeName.setProvider(...)`.
## Providers are async procs or lambdas that return with a Future[Result[seq[TypeName], string]].
## Notice MultiRequestBroker's `setProvider` return with a handler that can be used to remove the provider later (or error).
## You can register a request processor (provider) anywhere without the need to
## know who will request.
## Register provider functions with `TypeName.setProvider(...)`.
## Providers are async procs or lambdas that return `Future[Result[TypeName, string]]`.
## `setProvider` returns a handle (or an error) that can later be used to remove
## the provider.
## Requests can be made from anywhere with no direct dependency on the provider(s) by
## calling `TypeName.request()` - with arguments respecting the signature(s).
## This will asynchronously call the registered provider and return the collected data, in form of `Future[Result[seq[TypeName], string]]`.
## Requests can be made from anywhere with no direct dependency on the provider(s)
## by calling `TypeName.request()` (with arguments respecting the declared signature).
## This will asynchronously call all registered providers and return the collected
## responses as `Future[Result[seq[TypeName], string]]`.
##
## Whenever you don't want to process requests anymore (or your object instance that provides the request goes out of scope),
## you can remove it from the broker with `TypeName.removeProvider(handle)`.
@ -77,8 +103,9 @@ import std/[macros, strutils, tables, sugar]
import chronos
import results
import ./helper/broker_utils
import ./broker_context
export results, chronos
export results, chronos, broker_context
proc isReturnTypeValid(returnType, typeIdent: NimNode): bool =
## Accept Future[Result[TypeIdent, string]] as the contract.
@ -123,6 +150,13 @@ proc makeProcType(returnType: NimNode, params: seq[NimNode]): NimNode =
newTree(nnkProcTy, formal, pragmas)
proc ensureDistinctType(rhs: NimNode): NimNode =
## For PODs / aliases / externally-defined types, wrap in `distinct` unless
## it's already distinct.
if rhs.kind == nnkDistinctTy:
return copyNimTree(rhs)
newTree(nnkDistinctTy, copyNimTree(rhs))
macro MultiRequestBroker*(body: untyped): untyped =
when defined(requestBrokerDebug):
echo body.treeRepr
@ -134,11 +168,42 @@ macro MultiRequestBroker*(body: untyped): untyped =
for def in stmt:
if def.kind != nnkTypeDef:
continue
if not typeIdent.isNil():
error("Only one type may be declared inside MultiRequestBroker", def)
typeIdent = baseTypeIdent(def[0])
let rhs = def[2]
var objectType: NimNode
case rhs.kind
of nnkObjectTy:
objectType = rhs
let objectType = rhs
let recList = objectType[2]
if recList.kind != nnkRecList:
error(
"MultiRequestBroker object must declare a standard field list", objectType
)
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
error(
"MultiRequestBroker object definition only supports simple field declarations",
field,
)
objectDef = newTree(
nnkObjectTy,
copyNimTree(objectType[0]),
copyNimTree(objectType[1]),
exportedRecList,
)
of nnkRefTy:
isRefObject = true
if rhs.len != 1 or rhs[0].kind != nnkObjectTy:
@ -146,45 +211,44 @@ macro MultiRequestBroker*(body: untyped): untyped =
"MultiRequestBroker ref object must wrap a concrete object definition",
rhs,
)
objectType = rhs[0]
else:
continue
if not typeIdent.isNil():
error("Only one object type may be declared inside MultiRequestBroker", def)
typeIdent = baseTypeIdent(def[0])
let recList = objectType[2]
if recList.kind != nnkRecList:
error(
"MultiRequestBroker object must declare a standard field list", objectType
)
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
let objectType = rhs[0]
let recList = objectType[2]
if recList.kind != nnkRecList:
error(
"MultiRequestBroker object definition only supports simple field declarations",
field,
"MultiRequestBroker object must declare a standard field list", objectType
)
let exportedObjectType = newTree(
nnkObjectTy,
copyNimTree(objectType[0]),
copyNimTree(objectType[1]),
exportedRecList,
)
if isRefObject:
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
error(
"MultiRequestBroker object definition only supports simple field declarations",
field,
)
let exportedObjectType = newTree(
nnkObjectTy,
copyNimTree(objectType[0]),
copyNimTree(objectType[1]),
exportedRecList,
)
objectDef = newTree(nnkRefTy, exportedObjectType)
else:
objectDef = exportedObjectType
# Native type / alias / externally-defined type.
# Ensure we create a unique request type by wrapping in `distinct` unless
# the user already did.
objectDef = ensureDistinctType(rhs)
isRefObject = false
if typeIdent.isNil():
error("MultiRequestBroker body must declare exactly one object type", body)
error("MultiRequestBroker body must declare exactly one type", body)
when defined(requestBrokerDebug):
echo "MultiRequestBroker generating type: ", $typeIdent
@ -193,12 +257,13 @@ macro MultiRequestBroker*(body: untyped): untyped =
let sanitized = sanitizeIdentName(typeIdent)
let typeNameLit = newLit($typeIdent)
let isRefObjectLit = newLit(isRefObject)
let tableSym = bindSym"Table"
let initTableSym = bindSym"initTable"
let uint64Ident = ident("uint64")
let providerKindIdent = ident(sanitized & "ProviderKind")
let providerHandleIdent = ident(sanitized & "ProviderHandle")
let exportedProviderHandleIdent = postfix(copyNimTree(providerHandleIdent), "*")
let bucketTypeIdent = ident(sanitized & "CtxBucket")
let findBucketIdxIdent = ident(sanitized & "FindBucketIdx")
let getOrCreateBucketIdxIdent = ident(sanitized & "GetOrCreateBucketIdx")
let zeroKindIdent = ident("pk" & sanitized & "NoArgs")
let argKindIdent = ident("pk" & sanitized & "WithArgs")
var zeroArgSig: NimNode = nil
@ -306,63 +371,90 @@ macro MultiRequestBroker*(body: untyped): untyped =
let procType = makeProcType(returnType, cloneParams(argParams))
typeSection.add(newTree(nnkTypeDef, argProviderName, newEmptyNode(), procType))
var brokerRecList = newTree(nnkRecList)
var bucketRecList = newTree(nnkRecList)
bucketRecList.add(
newTree(nnkIdentDefs, ident("brokerCtx"), ident("BrokerContext"), newEmptyNode())
)
if not zeroArgSig.isNil():
brokerRecList.add(
bucketRecList.add(
newTree(
nnkIdentDefs,
zeroArgFieldName,
newTree(nnkBracketExpr, tableSym, uint64Ident, zeroArgProviderName),
newTree(nnkBracketExpr, ident("seq"), zeroArgProviderName),
newEmptyNode(),
)
)
if not argSig.isNil():
brokerRecList.add(
bucketRecList.add(
newTree(
nnkIdentDefs,
argFieldName,
newTree(nnkBracketExpr, tableSym, uint64Ident, argProviderName),
newTree(nnkBracketExpr, ident("seq"), argProviderName),
newEmptyNode(),
)
)
brokerRecList.add(newTree(nnkIdentDefs, ident("nextId"), uint64Ident, newEmptyNode()))
let brokerTypeIdent = ident(sanitizeIdentName(typeIdent) & "Broker")
let brokerTypeDef = newTree(
nnkTypeDef,
brokerTypeIdent,
newEmptyNode(),
typeSection.add(
newTree(
nnkRefTy, newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), brokerRecList)
),
nnkTypeDef,
bucketTypeIdent,
newEmptyNode(),
newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), bucketRecList),
)
)
var brokerRecList = newTree(nnkRecList)
brokerRecList.add(
newTree(
nnkIdentDefs,
ident("buckets"),
newTree(nnkBracketExpr, ident("seq"), bucketTypeIdent),
newEmptyNode(),
)
)
let brokerTypeIdent = ident(sanitizeIdentName(typeIdent) & "Broker")
typeSection.add(
newTree(
nnkTypeDef,
brokerTypeIdent,
newEmptyNode(),
newTree(
nnkRefTy, newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), brokerRecList)
),
)
)
typeSection.add(brokerTypeDef)
result = newStmtList()
result.add(typeSection)
let globalVarIdent = ident("g" & sanitizeIdentName(typeIdent) & "Broker")
let accessProcIdent = ident("access" & sanitizeIdentName(typeIdent) & "Broker")
var initStatements = newStmtList()
if not zeroArgSig.isNil():
initStatements.add(
quote do:
`globalVarIdent`.`zeroArgFieldName` =
`initTableSym`[`uint64Ident`, `zeroArgProviderName`]()
)
if not argSig.isNil():
initStatements.add(
quote do:
`globalVarIdent`.`argFieldName` =
`initTableSym`[`uint64Ident`, `argProviderName`]()
)
result.add(
quote do:
var `globalVarIdent` {.threadvar.}: `brokerTypeIdent`
proc `findBucketIdxIdent`(
broker: `brokerTypeIdent`, brokerCtx: BrokerContext
): int =
if brokerCtx == DefaultBrokerContext:
return 0
for i in 1 ..< broker.buckets.len:
if broker.buckets[i].brokerCtx == brokerCtx:
return i
return -1
proc `getOrCreateBucketIdxIdent`(
broker: `brokerTypeIdent`, brokerCtx: BrokerContext
): int =
let idx = `findBucketIdxIdent`(broker, brokerCtx)
if idx >= 0:
return idx
broker.buckets.add(`bucketTypeIdent`(brokerCtx: brokerCtx))
return broker.buckets.high
proc `accessProcIdent`(): `brokerTypeIdent` =
if `globalVarIdent`.isNil():
new(`globalVarIdent`)
`globalVarIdent`.nextId = 1'u64
`initStatements`
`globalVarIdent`.buckets =
@[`bucketTypeIdent`(brokerCtx: DefaultBrokerContext)]
return `globalVarIdent`
)
@ -372,40 +464,47 @@ macro MultiRequestBroker*(body: untyped): untyped =
result.add(
quote do:
proc setProvider*(
_: typedesc[`typeIdent`], handler: `zeroArgProviderName`
_: typedesc[`typeIdent`],
brokerCtx: BrokerContext,
handler: `zeroArgProviderName`,
): Result[`providerHandleIdent`, string] =
if handler.isNil():
return err("Provider handler must be provided")
let broker = `accessProcIdent`()
if broker.nextId == 0'u64:
broker.nextId = 1'u64
for existingId, existing in broker.`zeroArgFieldName`.pairs:
if existing == handler:
return ok(`providerHandleIdent`(id: existingId, kind: `zeroKindIdent`))
let newId = broker.nextId
inc broker.nextId
broker.`zeroArgFieldName`[newId] = handler
return ok(`providerHandleIdent`(id: newId, kind: `zeroKindIdent`))
let bucketIdx = `getOrCreateBucketIdxIdent`(broker, brokerCtx)
for i, existing in broker.buckets[bucketIdx].`zeroArgFieldName`:
if not existing.isNil() and existing == handler:
return ok(`providerHandleIdent`(id: uint64(i + 1), kind: `zeroKindIdent`))
broker.buckets[bucketIdx].`zeroArgFieldName`.add(handler)
return ok(
`providerHandleIdent`(
id: uint64(broker.buckets[bucketIdx].`zeroArgFieldName`.len),
kind: `zeroKindIdent`,
)
)
proc setProvider*(
_: typedesc[`typeIdent`], handler: `zeroArgProviderName`
): Result[`providerHandleIdent`, string] =
return setProvider(`typeIdent`, DefaultBrokerContext, handler)
)
clearBody.add(
quote do:
let broker = `accessProcIdent`()
if not broker.isNil() and broker.`zeroArgFieldName`.len > 0:
broker.`zeroArgFieldName`.clear()
)
result.add(
quote do:
proc request*(
_: typedesc[`typeIdent`]
_: typedesc[`typeIdent`], brokerCtx: BrokerContext
): Future[Result[seq[`typeIdent`], string]] {.async: (raises: []), gcsafe.} =
var aggregated: seq[`typeIdent`] = @[]
let providers = `accessProcIdent`().`zeroArgFieldName`
let broker = `accessProcIdent`()
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return ok(aggregated)
let providers = broker.buckets[bucketIdx].`zeroArgFieldName`
if providers.len == 0:
return ok(aggregated)
# var providersFut: seq[Future[Result[`typeIdent`, string]]] = collect:
var providersFut = collect(newSeq):
for provider in providers.values:
for provider in providers:
if provider.isNil():
continue
provider()
@ -435,32 +534,40 @@ macro MultiRequestBroker*(body: untyped): untyped =
return ok(aggregated)
proc request*(
_: typedesc[`typeIdent`]
): Future[Result[seq[`typeIdent`], string]] =
return request(`typeIdent`, DefaultBrokerContext)
)
if not argSig.isNil():
result.add(
quote do:
proc setProvider*(
_: typedesc[`typeIdent`], handler: `argProviderName`
_: typedesc[`typeIdent`],
brokerCtx: BrokerContext,
handler: `argProviderName`,
): Result[`providerHandleIdent`, string] =
if handler.isNil():
return err("Provider handler must be provided")
let broker = `accessProcIdent`()
if broker.nextId == 0'u64:
broker.nextId = 1'u64
for existingId, existing in broker.`argFieldName`.pairs:
if existing == handler:
return ok(`providerHandleIdent`(id: existingId, kind: `argKindIdent`))
let newId = broker.nextId
inc broker.nextId
broker.`argFieldName`[newId] = handler
return ok(`providerHandleIdent`(id: newId, kind: `argKindIdent`))
let bucketIdx = `getOrCreateBucketIdxIdent`(broker, brokerCtx)
for i, existing in broker.buckets[bucketIdx].`argFieldName`:
if not existing.isNil() and existing == handler:
return ok(`providerHandleIdent`(id: uint64(i + 1), kind: `argKindIdent`))
broker.buckets[bucketIdx].`argFieldName`.add(handler)
return ok(
`providerHandleIdent`(
id: uint64(broker.buckets[bucketIdx].`argFieldName`.len),
kind: `argKindIdent`,
)
)
proc setProvider*(
_: typedesc[`typeIdent`], handler: `argProviderName`
): Result[`providerHandleIdent`, string] =
return setProvider(`typeIdent`, DefaultBrokerContext, handler)
)
clearBody.add(
quote do:
let broker = `accessProcIdent`()
if not broker.isNil() and broker.`argFieldName`.len > 0:
broker.`argFieldName`.clear()
)
let requestParamDefs = cloneParams(argParams)
let argNameIdents = collectParamNames(requestParamDefs)
@ -481,17 +588,24 @@ macro MultiRequestBroker*(body: untyped): untyped =
newEmptyNode(),
)
)
formalParams.add(
newTree(nnkIdentDefs, ident("brokerCtx"), ident("BrokerContext"), newEmptyNode())
)
for paramDef in requestParamDefs:
formalParams.add(paramDef)
let requestPragmas = quote:
{.async: (raises: []), gcsafe.}
let requestBody = quote:
var aggregated: seq[`typeIdent`] = @[]
let providers = `accessProcIdent`().`argFieldName`
let broker = `accessProcIdent`()
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return ok(aggregated)
let providers = broker.buckets[bucketIdx].`argFieldName`
if providers.len == 0:
return ok(aggregated)
var providersFut = collect(newSeq):
for provider in providers.values:
for provider in providers:
if provider.isNil():
continue
let `providerSym` = provider
@ -531,53 +645,208 @@ macro MultiRequestBroker*(body: untyped): untyped =
)
)
result.add(
quote do:
proc clearProviders*(_: typedesc[`typeIdent`]) =
`clearBody`
let broker = `accessProcIdent`()
if not broker.isNil():
broker.nextId = 1'u64
)
let removeHandleSym = genSym(nskParam, "handle")
let removeBrokerSym = genSym(nskLet, "broker")
var removeBody = newStmtList()
removeBody.add(
quote do:
if `removeHandleSym`.id == 0'u64:
return
let `removeBrokerSym` = `accessProcIdent`()
if `removeBrokerSym`.isNil():
return
)
if not zeroArgSig.isNil():
removeBody.add(
# Backward-compatible default-context overload (no brokerCtx parameter).
var formalParamsDefault = newTree(nnkFormalParams)
formalParamsDefault.add(
quote do:
if `removeHandleSym`.kind == `zeroKindIdent`:
`removeBrokerSym`.`zeroArgFieldName`.del(`removeHandleSym`.id)
return
Future[Result[seq[`typeIdent`], string]]
)
if not argSig.isNil():
removeBody.add(
quote do:
if `removeHandleSym`.kind == `argKindIdent`:
`removeBrokerSym`.`argFieldName`.del(`removeHandleSym`.id)
return
formalParamsDefault.add(
newTree(
nnkIdentDefs,
ident("_"),
newTree(nnkBracketExpr, ident("typedesc"), copyNimTree(typeIdent)),
newEmptyNode(),
)
)
removeBody.add(
quote do:
discard
)
result.add(
quote do:
proc removeProvider*(
_: typedesc[`typeIdent`], `removeHandleSym`: `providerHandleIdent`
) =
`removeBody`
for paramDef in requestParamDefs:
formalParamsDefault.add(copyNimTree(paramDef))
)
var wrapperCall = newCall(ident("request"))
wrapperCall.add(copyNimTree(typeIdent))
wrapperCall.add(ident("DefaultBrokerContext"))
for argName in argNameIdents:
wrapperCall.add(copyNimTree(argName))
result.add(
newTree(
nnkProcDef,
postfix(ident("request"), "*"),
newEmptyNode(),
newEmptyNode(),
formalParamsDefault,
newEmptyNode(),
newEmptyNode(),
newStmtList(newTree(nnkReturnStmt, wrapperCall)),
)
)
let removeHandleCtxSym = genSym(nskParam, "handle")
let removeHandleDefaultSym = genSym(nskParam, "handle")
when true:
# Generate clearProviders / removeProvider with macro-time knowledge about which
# provider lists exist (zero-arg and/or arg providers).
if not zeroArgSig.isNil() and not argSig.isNil():
result.add(
quote do:
proc clearProviders*(_: typedesc[`typeIdent`], brokerCtx: BrokerContext) =
let broker = `accessProcIdent`()
if broker.isNil():
return
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return
broker.buckets[bucketIdx].`zeroArgFieldName`.setLen(0)
broker.buckets[bucketIdx].`argFieldName`.setLen(0)
if brokerCtx != DefaultBrokerContext:
broker.buckets.delete(bucketIdx)
proc clearProviders*(_: typedesc[`typeIdent`]) =
clearProviders(`typeIdent`, DefaultBrokerContext)
proc removeProvider*(
_: typedesc[`typeIdent`],
brokerCtx: BrokerContext,
`removeHandleCtxSym`: `providerHandleIdent`,
) =
if `removeHandleCtxSym`.id == 0'u64:
return
let broker = `accessProcIdent`()
if broker.isNil():
return
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return
if `removeHandleCtxSym`.kind == `zeroKindIdent`:
let idx = int(`removeHandleCtxSym`.id) - 1
if idx >= 0 and idx < broker.buckets[bucketIdx].`zeroArgFieldName`.len:
broker.buckets[bucketIdx].`zeroArgFieldName`[idx] = nil
elif `removeHandleCtxSym`.kind == `argKindIdent`:
let idx = int(`removeHandleCtxSym`.id) - 1
if idx >= 0 and idx < broker.buckets[bucketIdx].`argFieldName`.len:
broker.buckets[bucketIdx].`argFieldName`[idx] = nil
if brokerCtx != DefaultBrokerContext:
var hasAny = false
for p in broker.buckets[bucketIdx].`zeroArgFieldName`:
if not p.isNil():
hasAny = true
break
if not hasAny:
for p in broker.buckets[bucketIdx].`argFieldName`:
if not p.isNil():
hasAny = true
break
if not hasAny:
broker.buckets.delete(bucketIdx)
proc removeProvider*(
_: typedesc[`typeIdent`], `removeHandleDefaultSym`: `providerHandleIdent`
) =
removeProvider(`typeIdent`, DefaultBrokerContext, `removeHandleDefaultSym`)
)
elif not zeroArgSig.isNil():
result.add(
quote do:
proc clearProviders*(_: typedesc[`typeIdent`], brokerCtx: BrokerContext) =
let broker = `accessProcIdent`()
if broker.isNil():
return
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return
broker.buckets[bucketIdx].`zeroArgFieldName`.setLen(0)
if brokerCtx != DefaultBrokerContext:
broker.buckets.delete(bucketIdx)
proc clearProviders*(_: typedesc[`typeIdent`]) =
clearProviders(`typeIdent`, DefaultBrokerContext)
proc removeProvider*(
_: typedesc[`typeIdent`],
brokerCtx: BrokerContext,
`removeHandleCtxSym`: `providerHandleIdent`,
) =
if `removeHandleCtxSym`.id == 0'u64:
return
let broker = `accessProcIdent`()
if broker.isNil():
return
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return
if `removeHandleCtxSym`.kind != `zeroKindIdent`:
return
let idx = int(`removeHandleCtxSym`.id) - 1
if idx >= 0 and idx < broker.buckets[bucketIdx].`zeroArgFieldName`.len:
broker.buckets[bucketIdx].`zeroArgFieldName`[idx] = nil
if brokerCtx != DefaultBrokerContext:
var hasAny = false
for p in broker.buckets[bucketIdx].`zeroArgFieldName`:
if not p.isNil():
hasAny = true
break
if not hasAny:
broker.buckets.delete(bucketIdx)
proc removeProvider*(
_: typedesc[`typeIdent`], `removeHandleDefaultSym`: `providerHandleIdent`
) =
removeProvider(`typeIdent`, DefaultBrokerContext, `removeHandleDefaultSym`)
)
else:
result.add(
quote do:
proc clearProviders*(_: typedesc[`typeIdent`], brokerCtx: BrokerContext) =
let broker = `accessProcIdent`()
if broker.isNil():
return
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return
broker.buckets[bucketIdx].`argFieldName`.setLen(0)
if brokerCtx != DefaultBrokerContext:
broker.buckets.delete(bucketIdx)
proc clearProviders*(_: typedesc[`typeIdent`]) =
clearProviders(`typeIdent`, DefaultBrokerContext)
proc removeProvider*(
_: typedesc[`typeIdent`],
brokerCtx: BrokerContext,
`removeHandleCtxSym`: `providerHandleIdent`,
) =
if `removeHandleCtxSym`.id == 0'u64:
return
let broker = `accessProcIdent`()
if broker.isNil():
return
let bucketIdx = `findBucketIdxIdent`(broker, brokerCtx)
if bucketIdx < 0:
return
if `removeHandleCtxSym`.kind != `argKindIdent`:
return
let idx = int(`removeHandleCtxSym`.id) - 1
if idx >= 0 and idx < broker.buckets[bucketIdx].`argFieldName`.len:
broker.buckets[bucketIdx].`argFieldName`[idx] = nil
if brokerCtx != DefaultBrokerContext:
var hasAny = false
for p in broker.buckets[bucketIdx].`argFieldName`:
if not p.isNil():
hasAny = true
break
if not hasAny:
broker.buckets.delete(bucketIdx)
proc removeProvider*(
_: typedesc[`typeIdent`], `removeHandleDefaultSym`: `providerHandleIdent`
) =
removeProvider(`typeIdent`, DefaultBrokerContext, `removeHandleDefaultSym`)
)
when defined(requestBrokerDebug):
echo result.repr