diff --git a/README.md b/README.md index 0974c4c..cacb4fe 100644 --- a/README.md +++ b/README.md @@ -92,10 +92,7 @@ runActor("main") do (dataspace: Ref; turn: var Turn): ### [test_chat](./tests/test_chat.nim) Simple chat demo that is compatible with [chat.py](https://git.syndicate-lang.org/syndicate-lang/syndicate-py/src/branch/main/chat.py). ```sh -nim c -r tests/test_chat.nim \ - --cap:'' \ - --transport:'' \ - --user:fnord +SYNDICATE_ROUTE='] []>' nim c -r tests/test_chat.nim --user:fnord ``` ### [xdg_open_ng](https://git.syndicate-lang.org/ehmry/xdg_open_ng) diff --git a/lock.json b/lock.json index ec6713a..a613a20 100644 --- a/lock.json +++ b/lock.json @@ -1 +1 @@ -{"depends":[{"method":"fetchzip","path":"/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source","rev":"602c5d20c69c76137201b5d41f788f72afb95aa8","sha256":"1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65","url":"https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz","ref":"traditional-api","packages":["nimcrypto"],"srcDir":""},{"method":"fetchzip","path":"/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source","rev":"26d62fdc40feb84c6533956dc11d5ee9ea9b6c09","sha256":"0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh","url":"https://github.com/zevv/npeg/archive/26d62fdc40feb84c6533956dc11d5ee9ea9b6c09.tar.gz","ref":"1.2.1","packages":["npeg"],"srcDir":"src"},{"method":"fetchzip","path":"/nix/store/v03nzlpdgbfxd2zhcnkfbkq01d5kqxcl-source","rev":"84e0247555e4488594975900401baaf5bbbfb53","sha256":"1pfczsv8kl36qpv543f93d2y2vgz2acckssfap7l51s2x62m6qwx","url":"https://github.com/khchen/hashlib/archive/84e0247555e4488594975900401baaf5bbbfb53.tar.gz","packages":["hashlib"],"srcDir":""},{"method":"fetchzip","path":"/nix/store/nrxd0z8mxmdphw49c6p4n9lmmq0iq5pq-source","rev":"a2dc5becc0596d52ab205d869b7c167c0b562fb4","sha256":"09jygr7ynzh6vp2p54dgq2qz651d3lgvypkjwjp74zzp3jgwz7g5","url":"https://git.syndicate-lang.org/ehmry/preserves-nim/archive/a2dc5becc0596d52ab205d869b7c167c0b562fb4.tar.gz","ref":"20231019","packages":["preserves"],"srcDir":"src"}]} +{"depends":[{"method":"fetchzip","packages":["hashlib"],"path":"/nix/store/v03nzlpdgbfxd2zhcnkfbkq01d5kqxcl-source","rev":"84e0247555e4488594975900401baaf5bbbfb53","sha256":"1pfczsv8kl36qpv543f93d2y2vgz2acckssfap7l51s2x62m6qwx","srcDir":"","url":"https://github.com/khchen/hashlib/archive/84e0247555e4488594975900401baaf5bbbfb53.tar.gz"},{"method":"fetchzip","packages":["nimcrypto"],"path":"/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source","ref":"traditional-api","rev":"602c5d20c69c76137201b5d41f788f72afb95aa8","sha256":"1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65","srcDir":"","url":"https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"},{"method":"fetchzip","packages":["npeg"],"path":"/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source","ref":"1.2.1","rev":"26d62fdc40feb84c6533956dc11d5ee9ea9b6c09","sha256":"0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh","srcDir":"src","url":"https://github.com/zevv/npeg/archive/26d62fdc40feb84c6533956dc11d5ee9ea9b6c09.tar.gz"},{"method":"fetchzip","packages":["preserves"],"path":"/nix/store/nrxd0z8mxmdphw49c6p4n9lmmq0iq5pq-source","ref":"20231019","rev":"a2dc5becc0596d52ab205d869b7c167c0b562fb4","sha256":"09jygr7ynzh6vp2p54dgq2qz651d3lgvypkjwjp74zzp3jgwz7g5","srcDir":"src","url":"https://git.syndicate-lang.org/ehmry/preserves-nim/archive/a2dc5becc0596d52ab205d869b7c167c0b562fb4.tar.gz"}]} diff --git a/src/syndicate.nim b/src/syndicate.nim index 29ac173..ce13691 100644 --- a/src/syndicate.nim +++ b/src/syndicate.nim @@ -186,7 +186,8 @@ macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody: untyped) = `callbackProc` discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`)) -type BootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.} +type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.} +type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.} proc runActor*(name: string; bootProc: BootProc) = ## Run an `Actor` to completion. @@ -194,3 +195,8 @@ proc runActor*(name: string; bootProc: BootProc) = while not actor.future.finished: waitFor sleepAsync(500) read(actor.future) + +proc runActor*(name: string; bootProc: DeprecatedBootProc) {.deprecated.} = + ## Run an `Actor` to completion. + runActor(name) do (turn: var Turn, ds: Cap): + bootProc(ds, turn) diff --git a/src/syndicate/dataspaces.nim b/src/syndicate/dataspaces.nim index 43fab06..3085a48 100644 --- a/src/syndicate/dataspaces.nim +++ b/src/syndicate/dataspaces.nim @@ -37,9 +37,14 @@ method message(ds: Dataspace; turn: var Turn; a: AssertionRef) {.gcsafe.} = proc newDataspace*(turn: var Turn): Cap = newCap(turn, Dataspace(index: initIndex())) -type BootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.} +type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.} +type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.} proc bootDataspace*(name: string; bootProc: BootProc): Actor = bootActor(name) do (turn: var Turn): discard turn.facet.preventInertCheck() - bootProc(newDataspace(turn), turn) + bootProc(turn, newDataspace(turn)) + +proc bootDataspace*(name: string; bootProc: DeprecatedBootProc): Actor {.deprecated.} = + bootDataspace(name) do (turn: var Turn, ds: Cap): + bootProc(ds, turn) diff --git a/src/syndicate/relays.nim b/src/syndicate/relays.nim index 3e19d10..ea87818 100644 --- a/src/syndicate/relays.nim +++ b/src/syndicate/relays.nim @@ -2,8 +2,9 @@ # SPDX-License-Identifier: Unlicense import std/[asyncdispatch, options, streams, tables] +from std/os import getEnv, `/` import preserves -import ../syndicate, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress] +import ../syndicate, /capabilities, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress] when defined(traceSyndicate): when defined(posix): @@ -15,7 +16,10 @@ else: export `$` -type Oid = sturdy.Oid +type + Oid = sturdy.Oid + +export Stdio, Tcp, WebSocket, Unix type Value = Preserve[void] @@ -386,7 +390,7 @@ when defined(posix): const stdinReadSize = 128 - proc connectStdio*(ds: Cap; turn: var Turn) = + proc connectStdio*(turn: var Turn; ds: Cap) = ## Connect to an external dataspace over stdin and stdout. proc stdoutWriter(packet: sink Packet): Future[void] {.async.} = var buf = encode(packet) @@ -417,17 +421,34 @@ when defined(posix): asyncStdin.read(stdinReadSize).addCallback(readCb) asyncStdin.read(stdinReadSize).addCallback(readCb) + proc connectStdio*(ds: Cap; turn: var Turn) {.deprecated.} = connectStdio(turn, ds) + type BootProc* = proc (turn: var Turn; ds: Cap) {.gcsafe.} -proc resolve*(turn: var Turn; route: Route; bootProc: BootProc) = +proc envRoute*: Route[Cap] = + var text = getEnv("SYNDICATE_ROUTE") + if text == "": + var tx = (getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace").toPreserve(Cap) + result.transports = @[initRecord("unix", tx)] + result.pathSteps = @[capabilities.mint().toPreserve(Cap)] + else: + var pr = parsePreserves(text, Cap) + if not result.fromPreserve(pr): + raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $pr) + +proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) = var unix: Unix tcp: Tcp - if route.transports.len != 1 or route.pathSteps.len != 1: - raise newException(ValueError, "only a single transport and step supported for routes") + stdio: Stdio + doAssert(route.transports.len == 1, "only a single transport supported for routes") + doAssert(route.pathSteps.len < 2, "multiple path steps not supported for routes") if unix.fromPreserve route.transports[0]: connect(turn, unix, route.pathSteps[0], bootProc) elif tcp.fromPreserve route.transports[0]: connect(turn, tcp, route.pathSteps[0], bootProc) + elif stdio.fromPreserve route.transports[0]: + connectStdio(turn, ds) + bootProc(turn, ds) else: raise newException(ValueError, "unsupported route") diff --git a/syndicate.nimble b/syndicate.nimble index d2c540e..1005c3f 100644 --- a/syndicate.nimble +++ b/syndicate.nimble @@ -1,6 +1,6 @@ # Package -version = "20231020" +version = "20231021" author = "Emery Hemingway" description = "Syndicated actors for conversational concurrency" license = "Unlicense" diff --git a/tests/Tupfile b/tests/Tupfile index 144dd7a..944a36b 100644 --- a/tests/Tupfile +++ b/tests/Tupfile @@ -1,3 +1,3 @@ include_rules : foreach *.prs |> !preserves_schema_nim |> | {schema} -: foreach t*.nim | {schema} $(SYNDICATE_PROTOCOL) |> !nim_run |> +: foreach t*.nim | ../../preserves-nim/ {schema} $(SYNDICATE_PROTOCOL) |> !nim_run |> | ../ diff --git a/tests/test_chat.nim b/tests/test_chat.nim index da3e963..ab6124c 100644 --- a/tests/test_chat.nim +++ b/tests/test_chat.nim @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Unlicense import std/[asyncdispatch, asyncfile, os, parseopt] -import preserves, syndicate, syndicate/protocols/transportAddress +import preserves, syndicate, syndicate/relays type Present {.preservesRecord: "Present".} = object @@ -18,6 +18,7 @@ proc readStdin(facet: Facet; ds: Cap; username: string) = let future = readLine(file) addCallback(future, facet) do (turn: var Turn): var msg = read(future) + if msg == "": quit() message(turn, ds, Says(who: username, what: msg)) readLine() readLine() @@ -35,32 +36,18 @@ proc chat(turn: var Turn; ds: Cap; username: string) = readStdin(turn.facet, ds, username) proc main = - var - transport: Preserve[void] - cap: Preserve[Cap] - username = getEnv("USER") - calledWithArguments = false + let route = envRoute() + var username = "" + for kind, key, val in getopt(): - calledWithArguments = true if kind == cmdLongOption: case key - of "address", "transport": - transport = parsePreserves(val) - of "cap", "sturdy": - cap = parsePreserves(val, Cap) of "user", "username": username = val - if calledWithArguments: - runActor("chat") do (root: Cap; turn: var Turn): - var - unixAddr: transportAddress.Unix - tcpAddr: transportAddress.Tcp - if fromPreserve(unixAddr, transport): - connect(turn, unixAddr, cap) do (turn: var Turn; ds: Cap): - chat(turn, ds, username) - elif fromPreserve(tcpAddr, transport): - connect(turn, tcpAddr, cap) do (turn: var Turn; ds: Cap): - chat(turn, ds, username) + if username != "": + runActor("chat") do (turn: var Turn; root: Cap): + resolve(turn, root, route) do (turn: var Turn; ds: Cap): + chat(turn, ds, username) main()