diff --git a/lib/syndicate.sh b/lib/syndicate.sh index 9ba4dc7..27e057d 100755 --- a/lib/syndicate.sh +++ b/lib/syndicate.sh @@ -2,13 +2,37 @@ set -euo pipefail +# Set to an incoming TurnEvent by `ds_dispatch`. +# +ds_event= + +# Counter for allocating handle IDs and OIDs. (We could have separate +# counters for each of these, but why bother?) +# ds_handle=0 + +# Buffer of pending TurnEvents to be sent out. `ds_action` augments +# the buffer; `ds_flush` sends and resets it. +# ds_buf="" + +# These two map user-supplied names to previously-asserted handle IDs +# and previously-asserted target OIDs, respectively, for later use in +# `ds_retract`. +# declare -A ds_handles declare -A ds_handle_oids -declare -A ds_object_map -declare -A ds_retraction_handlers +# Maps local OIDs to callback code to execute when events arrive for +# that OID. +# +declare -A ds_object_map + +# ds_project +# +# Applies the Preserves-Path expression to the given term, writing the +# result(s) to stdout, one per line. +# ds_project() { # cargo install preserves-tools local input="$1" @@ -17,13 +41,27 @@ ds_project() { echo "$input" | preserves-tool convert --indent=no --select "$selector" "$@" } +# ds_connect_stdio +# +# Uses stdin for input from our peer, and stdout to send output to our +# peer, connecting them to fds 5 and 6 respectively before evalutating +# `code` and entering a mainloop. +# ds_connect_stdio() { - eval "$1" exec 5<&0 6>&1 + eval "$1" ds_flush ds_mainloop } +# ds_connect +# +# `addr` should match schema transportAddress.Tcp, +# transportAddress.Unix, or transportAddress.Stdio. Uses `nc` for the +# first two and `ds_connect_stdio` for the last. Causes `code` to be +# evaluated in a context where fds 5 and 6 are for input from and +# output to the peer, respectively, before entering a mainloop. +# ds_connect() { local addr=$1 case $(ds_project "$addr" ".^") in @@ -44,11 +82,20 @@ ds_connect() { } 5< <(nc $nc_args <$f) 6>$f } +# ds_send +# +# Transmits `data` immediately to the peer via fd 6. +# ds_send() { echo "$@" >&6 # echo "SENDING: $@" } +# ds_mainloop +# +# Repeatedly reads a line from the peer (fd 5) and dispatches it to a +# local object. +# ds_mainloop() { while read ds_packet do @@ -61,11 +108,22 @@ ds_mainloop() { done <&5 } +# ds_dispatch +# +# Sets `ds_event` to `event`. Then, looks up and executes local object +# code using `localoid`. +# ds_dispatch() { ds_event="$2" eval "${ds_object_map[$1]}" } +# ds_action +# +# `wireref` should be a sturdy.WireRef. If it's a `mine`, dispatches +# `event` locally via `ds_dispatch`; if it's a `yours`, adds `event` +# to `ds_buf` (for a subsequent `ds_flush` to actually transmit). +# ds_action() { case $(ds_project "$1" '.embedded . 0') in 0) ds_dispatch "$(ds_project "$1" '.embedded . 1')" "$2" ;; @@ -73,6 +131,10 @@ ds_action() { esac } +# ds_flush +# +# Transmits (and resets) `ds_buf` if it's non-empty. +# ds_flush() { if [ -n "$ds_buf" ] then @@ -81,6 +143,12 @@ ds_flush() { fi } +# ds_assert [] +# +# Asserts `term` to `wireref`. If `localname` is supplied, records +# enough information to allow `localname` to be used with `ds_retract` +# later to retract the assertion. +# ds_assert() { local h="$ds_handle" ds_handle=$(($ds_handle + 1)) @@ -92,14 +160,29 @@ ds_assert() { fi } +# ds_retract +# +# Retracts a previously-asserted term using a `localname` supplied to +# `ds_assert`. +# ds_retract() { ds_action "${ds_handle_oids[$1]}" "" } +# ds_message +# +# Sends `term` to `wireref`. +# ds_message() { ds_action "$1" "" } +# ds_object +# +# Allocates a fresh local OID, and registers `code` to be called when +# an event arrives for it. Stores a `wireref` for the new OID in the +# local variable named `varname`. +# ds_object() { local oid="$ds_handle" ds_handle=$(($ds_handle + 1))