Move duplicated and common code into broker_utils from event- request- and multi_request_brokers

This commit is contained in:
NagyZoltanPeter 2025-12-17 08:58:40 +01:00
parent ca96391255
commit 26f6dbefa2
No known key found for this signature in database
GPG Key ID: 3E1F97CF4A7B6F42
4 changed files with 176 additions and 306 deletions

View File

@ -90,103 +90,15 @@ import ./helper/broker_utils, broker_context
export chronicles, results, chronos, broker_context
proc ensureDistinctType(rhs: NimNode): NimNode =
## For PODs / aliases / externally-defined types, wrap in `distinct` unless
## it's already distinct.
if rhs.kind == nnkDistinctTy:
return rhs
newTree(nnkDistinctTy, copyNimTree(rhs))
macro EventBroker*(body: untyped): untyped =
when defined(eventBrokerDebug):
echo body.treeRepr
var typeIdent: NimNode = nil
var objectDef: NimNode = nil
var fieldNames: seq[NimNode] = @[]
var fieldTypes: seq[NimNode] = @[]
var hasInlineFields = false
for stmt in body:
if stmt.kind == nnkTypeSection:
for def in stmt:
if def.kind != nnkTypeDef:
continue
let rhs = def[2]
if not typeIdent.isNil():
error("Only one type may be declared inside EventBroker", def)
typeIdent = baseTypeIdent(def[0])
# Inline object/ref object definitions.
case rhs.kind
of nnkObjectTy:
let recList = rhs[2]
if recList.kind != nnkRecList:
error("EventBroker object must declare a standard field list", rhs)
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
let fieldTypeNode = field[field.len - 2]
for i in 0 ..< field.len - 2:
let baseFieldIdent = baseTypeIdent(field[i])
fieldNames.add(copyNimTree(baseFieldIdent))
fieldTypes.add(copyNimTree(fieldTypeNode))
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
error(
"EventBroker object definition only supports simple field declarations",
field,
)
let exportedObjectType = newTree(
nnkObjectTy, copyNimTree(rhs[0]), copyNimTree(rhs[1]), exportedRecList
)
objectDef = exportedObjectType
hasInlineFields = true
of nnkRefTy:
if rhs.len != 1 or rhs[0].kind != nnkObjectTy:
error("EventBroker ref object must wrap a concrete object definition", rhs)
let obj = rhs[0]
let recList = obj[2]
if recList.kind != nnkRecList:
error("EventBroker object must declare a standard field list", obj)
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
let fieldTypeNode = field[field.len - 2]
for i in 0 ..< field.len - 2:
let baseFieldIdent = baseTypeIdent(field[i])
fieldNames.add(copyNimTree(baseFieldIdent))
fieldTypes.add(copyNimTree(fieldTypeNode))
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
error(
"EventBroker object definition only supports simple field declarations",
field,
)
let exportedObjectType = newTree(
nnkObjectTy, copyNimTree(obj[0]), copyNimTree(obj[1]), exportedRecList
)
objectDef = newTree(nnkRefTy, exportedObjectType)
hasInlineFields = true
else:
# Native type / alias / externally-defined type.
# Ensure we create a unique event type by wrapping in `distinct` unless
# the user already did.
objectDef = ensureDistinctType(rhs)
if typeIdent.isNil():
error("EventBroker body must declare exactly one type", body)
let parsed = parseSingleTypeDef(body, "EventBroker", collectFieldInfo = true)
let typeIdent = parsed.typeIdent
let objectDef = parsed.objectDef
let fieldNames = parsed.fieldNames
let fieldTypes = parsed.fieldTypes
let hasInlineFields = parsed.hasInlineFields
let exportedTypeIdent = postfix(copyNimTree(typeIdent), "*")
let sanitized = sanitizeIdentName(typeIdent)

View File

@ -1,5 +1,21 @@
import std/macros
type ParsedBrokerType* = object
## Result of parsing the single `type` definition inside a broker macro body.
##
## - `typeIdent`: base identifier for the declared type name
## - `objectDef`: exported type definition RHS (inline object fields exported;
## non-object types wrapped in `distinct` unless already distinct)
## - `isRefObject`: true only for inline `ref object` definitions
## - `hasInlineFields`: true for inline `object` / `ref object`
## - `fieldNames`/`fieldTypes`: populated only when `collectFieldInfo = true`
typeIdent*: NimNode
objectDef*: NimNode
isRefObject*: bool
hasInlineFields*: bool
fieldNames*: seq[NimNode]
fieldTypes*: seq[NimNode]
proc sanitizeIdentName*(node: NimNode): string =
var raw = $node
var sanitizedName = newStringOfCap(raw.len)
@ -41,3 +57,150 @@ proc baseTypeIdent*(defName: NimNode): NimNode =
baseTypeIdent(defName[0])
else:
error("Unsupported type name in broker definition", defName)
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))
proc cloneParams*(params: seq[NimNode]): seq[NimNode] =
## Deep copy parameter definitions so they can be inserted in multiple places.
result = @[]
for param in params:
result.add(copyNimTree(param))
proc collectParamNames*(params: seq[NimNode]): seq[NimNode] =
## Extract all identifier symbols declared across IdentDefs nodes.
result = @[]
for param in params:
assert param.kind == nnkIdentDefs
for i in 0 ..< param.len - 2:
let nameNode = param[i]
if nameNode.kind == nnkEmpty:
continue
result.add(ident($nameNode))
proc parseSingleTypeDef*(
body: NimNode,
macroName: string,
allowRefToNonObject = false,
collectFieldInfo = false,
): ParsedBrokerType =
## Parses exactly one `type` definition from a broker macro body.
##
## Supported RHS:
## - inline `object` / `ref object` (fields are auto-exported)
## - non-object types / aliases / externally-defined types (wrapped in `distinct`)
## - optionally: `ref SomeType` when `allowRefToNonObject = true`
var typeIdent: NimNode = nil
var objectDef: NimNode = nil
var isRefObject = false
var hasInlineFields = false
var fieldNames: seq[NimNode] = @[]
var fieldTypes: seq[NimNode] = @[]
for stmt in body:
if stmt.kind != nnkTypeSection:
continue
for def in stmt:
if def.kind != nnkTypeDef:
continue
if not typeIdent.isNil():
error("Only one type may be declared inside " & macroName, def)
typeIdent = baseTypeIdent(def[0])
let rhs = def[2]
case rhs.kind
of nnkObjectTy:
let recList = rhs[2]
if recList.kind != nnkRecList:
error(macroName & " object must declare a standard field list", rhs)
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
if collectFieldInfo:
let fieldTypeNode = field[field.len - 2]
for i in 0 ..< field.len - 2:
let baseFieldIdent = baseTypeIdent(field[i])
fieldNames.add(copyNimTree(baseFieldIdent))
fieldTypes.add(copyNimTree(fieldTypeNode))
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
error(
macroName & " object definition only supports simple field declarations",
field,
)
objectDef = newTree(
nnkObjectTy, copyNimTree(rhs[0]), copyNimTree(rhs[1]), exportedRecList
)
isRefObject = false
hasInlineFields = true
of nnkRefTy:
if rhs.len != 1:
error(macroName & " ref type must have a single base", rhs)
if rhs[0].kind == nnkObjectTy:
let obj = rhs[0]
let recList = obj[2]
if recList.kind != nnkRecList:
error(macroName & " object must declare a standard field list", obj)
var exportedRecList = newTree(nnkRecList)
for field in recList:
case field.kind
of nnkIdentDefs:
ensureFieldDef(field)
if collectFieldInfo:
let fieldTypeNode = field[field.len - 2]
for i in 0 ..< field.len - 2:
let baseFieldIdent = baseTypeIdent(field[i])
fieldNames.add(copyNimTree(baseFieldIdent))
fieldTypes.add(copyNimTree(fieldTypeNode))
var cloned = copyNimTree(field)
for i in 0 ..< cloned.len - 2:
cloned[i] = exportIdentNode(cloned[i])
exportedRecList.add(cloned)
of nnkEmpty:
discard
else:
error(
macroName & " object definition only supports simple field declarations",
field,
)
let exportedObjectType = newTree(
nnkObjectTy, copyNimTree(obj[0]), copyNimTree(obj[1]), exportedRecList
)
objectDef = newTree(nnkRefTy, exportedObjectType)
isRefObject = true
hasInlineFields = true
elif allowRefToNonObject:
## `ref SomeType` (SomeType can be defined elsewhere)
objectDef = ensureDistinctType(rhs)
isRefObject = false
hasInlineFields = false
else:
error(macroName & " ref object must wrap a concrete object definition", rhs)
else:
## Non-object type / alias.
objectDef = ensureDistinctType(rhs)
isRefObject = false
hasInlineFields = false
if typeIdent.isNil():
error(macroName & " body must declare exactly one type", body)
result = ParsedBrokerType(
typeIdent: typeIdent,
objectDef: objectDef,
isRefObject: isRefObject,
hasInlineFields: hasInlineFields,
fieldNames: fieldNames,
fieldTypes: fieldTypes,
)

View File

@ -122,23 +122,6 @@ proc isReturnTypeValid(returnType, typeIdent: NimNode): bool =
return false
inner[2].kind == nnkIdent and inner[2].eqIdent("string")
proc cloneParams(params: seq[NimNode]): seq[NimNode] =
## Deep copy parameter definitions so they can be reused in generated nodes.
result = @[]
for param in params:
result.add(copyNimTree(param))
proc collectParamNames(params: seq[NimNode]): seq[NimNode] =
## Extract identifiers declared in parameter definitions.
result = @[]
for param in params:
assert param.kind == nnkIdentDefs
for i in 0 ..< param.len - 2:
let nameNode = param[i]
if nameNode.kind == nnkEmpty:
continue
result.add(ident($nameNode))
proc makeProcType(returnType: NimNode, params: seq[NimNode]): NimNode =
var formal = newTree(nnkFormalParams)
formal.add(returnType)
@ -150,105 +133,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
var typeIdent: NimNode = nil
var objectDef: NimNode = nil
var isRefObject = false
for stmt in body:
if stmt.kind == nnkTypeSection:
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]
case rhs.kind
of nnkObjectTy:
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:
error(
"MultiRequestBroker ref object must wrap a concrete object definition",
rhs,
)
let objectType = rhs[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:
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:
# 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 type", body)
let parsed = parseSingleTypeDef(body, "MultiRequestBroker")
let typeIdent = parsed.typeIdent
let objectDef = parsed.objectDef
let isRefObject = parsed.isRefObject
when defined(requestBrokerDebug):
echo "MultiRequestBroker generating type: ", $typeIdent

View File

@ -206,23 +206,6 @@ proc isReturnTypeValid(returnType, typeIdent: NimNode, mode: RequestBrokerMode):
of rbSync:
isSyncReturnTypeValid(returnType, typeIdent)
proc cloneParams(params: seq[NimNode]): seq[NimNode] =
## Deep copy parameter definitions so they can be inserted in multiple places.
result = @[]
for param in params:
result.add(copyNimTree(param))
proc collectParamNames(params: seq[NimNode]): seq[NimNode] =
## Extract all identifier symbols declared across IdentDefs nodes.
result = @[]
for param in params:
assert param.kind == nnkIdentDefs
for i in 0 ..< param.len - 2:
let nameNode = param[i]
if nameNode.kind == nnkEmpty:
continue
result.add(ident($nameNode))
proc makeProcType(
returnType: NimNode, params: seq[NimNode], mode: RequestBrokerMode
): NimNode =
@ -253,92 +236,13 @@ proc parseMode(modeNode: NimNode): RequestBrokerMode =
else:
error("RequestBroker mode must be `sync` or `async` (default is async)", modeNode)
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))
proc generateRequestBroker(body: NimNode, mode: RequestBrokerMode): NimNode =
when defined(requestBrokerDebug):
echo body.treeRepr
echo "RequestBroker mode: ", $mode
var typeIdent: NimNode = nil
var objectDef: NimNode = nil
for stmt in body:
if stmt.kind == nnkTypeSection:
for def in stmt:
if def.kind != nnkTypeDef:
continue
if not typeIdent.isNil():
error("Only one type may be declared inside RequestBroker", def)
typeIdent = baseTypeIdent(def[0])
let rhs = def[2]
## Support inline object types (fields are auto-exported)
## AND non-object types / aliases (e.g. `string`, `int`, `OtherType`).
case rhs.kind
of nnkObjectTy:
let recList = rhs[2]
if recList.kind != nnkRecList:
error("RequestBroker object must declare a standard field list", rhs)
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(
"RequestBroker object definition only supports simple field declarations",
field,
)
objectDef = newTree(
nnkObjectTy, copyNimTree(rhs[0]), copyNimTree(rhs[1]), exportedRecList
)
of nnkRefTy:
if rhs.len != 1:
error("RequestBroker ref type must have a single base", rhs)
if rhs[0].kind == nnkObjectTy:
let obj = rhs[0]
let recList = obj[2]
if recList.kind != nnkRecList:
error("RequestBroker object must declare a standard field list", obj)
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(
"RequestBroker object definition only supports simple field declarations",
field,
)
let exportedObjectType = newTree(
nnkObjectTy, copyNimTree(obj[0]), copyNimTree(obj[1]), exportedRecList
)
objectDef = newTree(nnkRefTy, exportedObjectType)
else:
## `ref SomeType` (SomeType can be defined elsewhere)
objectDef = ensureDistinctType(rhs)
else:
## Non-object type / alias (e.g. `string`, `int`, `SomeExternalType`).
objectDef = ensureDistinctType(rhs)
if typeIdent.isNil():
error("RequestBroker body must declare exactly one type", body)
let parsed = parseSingleTypeDef(body, "RequestBroker", allowRefToNonObject = true)
let typeIdent = parsed.typeIdent
let objectDef = parsed.objectDef
when defined(requestBrokerDebug):
echo "RequestBroker generating type: ", $typeIdent