logos-delivery/logos_delivery/logos_delivery.nim
NagyZoltanPeter 6bd8873094
Wire KernelInterface onto Waku; non-API broker conversion
Squash of the kernel-wiring work (d86f0651..535fe2c9):
- Don't exercise FFI of Brokers
- Fix tests and examples to compile; verify product unchanged
- bump nim-brokers to v3.1.3
- Events dropListeners changed to async
- WIP + finalize KernelInterface <-> Waku wiring
2026-06-19 08:50:18 +02:00

210 lines
7.8 KiB
Nim

## LogosDelivery — the LogosDeliveryInterface facade implementation.
##
## Owns a `Waku` (node + messagingClient + reliableChannelManager) and exposes
## the three sub-interfaces through cached getters. Mirrors the persistence
## example's `PersistenceImpl` (nim-brokers/examples/persistence/nimlib): a
## `ref object of <iface>` with `BrokerImplement`, sub-instances built on a
## `newInstanceCtx(self.brokerCtx)`, plus a `provideFactory`.
##
## Scope (decided): `initializeRequest(configPath)` creates + mounts the node but
## does NOT start it. `kernel()` is a stub until a KernelImpl exists.
import results, chronos
import std/options # some()/Option for WakuNodeConf.mode
import std/json # newJArray/newJObject/%*/pretty in getAvailableConfigs
import brokers/broker_context, brokers/broker_interface, brokers/broker_implement
import logos_delivery/api/logos_delivery_interface as logosdelivery_iface
import logos_delivery/api/types as api_types
import logos_delivery/api/kernel_interface as kernel_iface
import
logos_delivery/waku/factory/waku,
logos_delivery/waku/factory/waku_state_info,
logos_delivery/messaging/messaging_client,
logos_delivery/channels/reliable_channel_manager
# Waku, getNodeInfoItem, mountMessagingClient, mountReliableChannelManager, stop
import tools/confutils/[cli_args, config_option_meta] # WakuNodeConf (+ .load)
type LogosDelivery* = ref object of LogosDeliveryInterface
waku: Waku ## the owned node facade (built in initializeRequest)
messagingClient: MessagingClient
reliableChannelManager: ReliableChannelManager
proc loadConf(configPath: string): Result[WakuNodeConf, string] =
## Delegates to cli_args' concrete loader so confutils' `load` macro expands in
## cli_args' full scope (avoids leaking undeclared identifiers / WakuMode clash here).
loadWakuNodeConfFromFile(configPath)
proc createNode(conf: WakuNodeConf): Future[Result[Waku, string]] {.async.} =
let wakuConf = conf.toWakuConf().valueOr:
return err("Failed to handle the configuration: " & error)
## We are not defining app callbacks at node creation
let wakuRes = (await Waku.createUnderContext(globalBrokerContext(), wakuConf)).valueOr:
error "waku initialization failed", error = error
return err("Failed setting up Waku: " & $error)
return ok(wakuRes)
proc initMessagingClient(self: LogosDelivery): Result[MessagingClient, string] =
return ok(
MessagingClient.createUnderContext(
globalBrokerContext(), self.waku.conf.p2pReliability, self.waku.node
)
)
proc initReliableChannelManager(
self: LogosDelivery
): Result[ReliableChannelManager, string] =
return ok(
ReliableChannelManager.createUnderContext(
globalBrokerContext(), self.messagingClient
)
)
BrokerImplement LogosDelivery of LogosDeliveryInterface:
method kernel(
self: LogosDelivery
): Future[Result[KernelInterface, string]] {.async.} =
if self.waku.isNil():
return err("not initialized; call startAsClient first")
return ok(KernelInterface(self.waku))
method messaging(
self: LogosDelivery
): Future[Result[MessagingClientInterface, string]] {.async.} =
if self.waku.isNil():
return err("not initialized; call startAsClient first")
if self.messagingClient.isNil():
return err("messaging client not mounted")
return ok(MessagingClientInterface(self.messagingClient))
method channels(
self: LogosDelivery
): Future[Result[ReliableChannelManagerInterface, string]] {.async.} =
if self.waku.isNil():
return err("not initialized; call startAsClient first")
if self.messagingClient.isNil():
return err("not initialized; call startAsClient first")
if self.reliableChannelManager.isNil():
self.reliableChannelManager = initReliableChannelManager(self).valueOr:
return err("failed to initialize ReliableChannelManager: " & error)
self.reliableChannelManager.start().isOkOr:
return err("failed to start ReliableChannelManager: " & error)
return ok(ReliableChannelManagerInterface(self.reliableChannelManager))
method startAsNode(
self: LogosDelivery, config: string
): Future[Result[void, string]] {.async.} =
if not self.waku.isNil():
return err("already initialized")
let conf = loadConf(config).valueOr:
return err("failed to load config: " & error)
# The restamp forces {.async: (raises: []).}, but createNode can raise.
try:
self.waku = (await createNode(conf)).valueOr:
return err("failed to create node: " & error)
(await self.waku.start()).isOkOr:
return err("failed to start node: " & $error)
return ok()
except CatchableError as e:
return err("initialize failed: " & e.msg)
method startAsClient(
self: LogosDelivery, mode: api_types.WakuMode, preset: string
): Future[Result[MessagingClientInterface, string]] {.async.} =
if not self.messagingClient.isNil():
return err("already initialized")
if not self.waku.isNil():
return err(
"already started as node; cannot start as client, but you can use as client"
)
try:
var conf: WakuNodeConf = ?defaultWakuNodeConf()
conf.mode = some(mode)
conf.preset = preset
self.waku = (await createNode(conf)).valueOr:
return err("failed to create node: " & error)
self.messagingClient = initMessagingClient(self).valueOr:
return err("failed to mount messaging client: " & error)
(await self.waku.start()).isOkOr:
return err("failed to start node: " & $error)
self.messagingClient.start().isOkOr:
return err("failed to start messaging client: " & $error)
return ok(MessagingClientInterface(self.messagingClient))
except CatchableError as e:
return err("initialize failed: " & e.msg)
method stop(self: LogosDelivery): Future[Result[void, string]] {.async.} =
if not self.reliableChannelManager.isNil():
try:
await self.reliableChannelManager.stop()
self.reliableChannelManager.close()
self.reliableChannelManager = nil
except CatchableError as e:
return err("ReliableChannelManager stop failed: " & e.msg)
if not self.messagingClient.isNil():
try:
await self.messagingClient.stop()
self.messagingClient.close()
self.messagingClient = nil
except CatchableError as e:
return err("MessagingClient stop failed: " & e.msg)
if not self.waku.isNil():
try:
(await self.waku.stop()).isOkOr:
return err("Node stop failed: " & $error)
# `Waku` is a plain ref object (not a BrokerImplement) — `stop()` is its
# only teardown; there is no `close()`.
self.waku = nil
except CatchableError as e:
return err("Node stop failed: " & e.msg)
return ok()
method shutdown(self: LogosDelivery): Future[Result[void, string]] {.async.} =
return await self.stop()
method getNodeInfo(
self: LogosDelivery, id: NodeInfoId
): Future[Result[string, string]] {.async.} =
return ok(self.waku.stateInfo.getNodeInfoItem(id))
method getAvailableConfigs(
self: LogosDelivery
): Future[Result[string, string]] {.async.} =
let optionMetas: seq[ConfigOptionMeta] = extractConfigOptionMeta(WakuNodeConf)
var configOptionDetails = newJArray()
for meta in optionMetas:
configOptionDetails.add(
%*{
meta.fieldName: meta.typeName & "(" & meta.defaultValue & ")",
"desc": meta.desc,
}
)
var jsonNode = newJObject()
jsonNode["configOptions"] = configOptionDetails
let asString = pretty(jsonNode)
return ok(pretty(jsonNode))
# DI factory registration: non ffi - nim lib users needs it.
LogosDeliveryInterface.provideFactory(
proc(): Result[LogosDeliveryInterface, string] {.gcsafe.} =
ok(LogosDeliveryInterface(LogosDelivery.createUnderContext(globalBrokerContext())))
)