Hi, would like to share small networking library to make it easier to build distributed Nim programs and APIs.
https://github.com/al6x/nim/tree/main/nodem
Video Demo RPC in 10 and REST API in 5 lines of Nim, 3min
Exporting some functions as available over network:
import nodem
proc plus(_: Node, a, b: float): float {.nexport.} = a + b
node"server".run_forever
And calling it from another process:
import nodem
proc plus*(node: Node, a: float, b: float): float {.nimport.} = discard
echo node"server".plus(3, 2)
# => 5
Connect/disconnect/reconnect handled automatically. Also, the client functions could be auto-generated, making the client code even shorter.
import nodem, nodem/httpm
proc plus(_: Node, a, b: float): float {.nexport.} = a + b
node"server".run_http_forever("http://localhost:8000", true)
# curl http://localhost:8000/plus/1?b=2
#
# => {"is_error":false,"result":3.0}
#
# - Arguments auto casted to correct types.
# - Both positional and named arguments supported.
The main use case is tens or hundreds of nodes in local network exchanging lots of small messages.
Would be interesting to hear feedback, especially about the macro and low-level TCP networking code.
feedback on the macro:
there's already a data structure for nim's ast, you're jumping through a lot of hoops converting an nnkProcDef into (NimNode,seq[(NimNode,NimNode,NimNode)],NimNode,bool) just so you can use tuple unpacking.
instead of taking them apart and putting them back together again, you could leave the nnkProcDef as is, changing the bits you need, which i believe is only the return type and the body.
quote do is a great help, but your code is at the point of complexity where using the more full-featured macro api is reasonable.
the big ol' nested if/case block where you have a separate branch for each number of arguments a function could have, whether its return type is void or not, and whether it's async or not, is (sorry) a bit of a mess.
if you left your fn_signature as an nnkProcDef you could basically modify it in place, but working with the deconstruction (putting aside async/void for the moment):
let argnames = args.mapIt(it[0])
let params = @[rtype] & args.mapIt(newIdentDefs(it[0],it[1]))
let body = newCall("call_nexport_fn",argnames)
result = newProc(fname,params,body)
the big ol' nested if/case block where you have a separate branch for each number of arguments a function could have, whether its return type is void or not, and whether it's async or not, is (sorry) a bit of a mess.
Yes, I don't like that code either. But I don't know macros well and it was looked to me like a safer and more predictable approach to use if/else and avoid writing complex macro. Going to refactor it, thanks for the suggestion! :)
Hello,
What version of nim did you use that the .to_json still works in ?
I have tried two different version of nim and they both fail to work at the .to_json
can you update the nodem github/codeberg so the source works again with nim?