1 import Base 2 import ./json.bend as Json 3 import ./cmd.bend as Cmd 4 import ./exec.bend as Exec 5 import ./fmt/out.bend as O 6 import ./fmt/yaml.bend as Yaml 7 import ./fmt/toon.bend as Toon 8 import ./frame.bend as Fr 9 import ./rpcmsg.bend as M 10 11 # One rpc message in, one response out, shared by `cswap rpc` (stdin/stdout) 12 # and `cswap serve` (a unix socket). The message is in the rpc format: JSON 13 # (a line), YAML or TOON (a block ended by a blank line). The command runs 14 # in this process through Exec.exec_line with the output format forced to json. 15 16 # A YAML or TOON message is a block, a JSON message a line. 17 def is_block(f: O.Fmt) -> Bool: 18 Bool.not(O.is_json(f)) 19 20 def render(f: O.Fmt, +j: Json.Json) -> String: 21 match f: 22 case O.FYaml{}: 23 Yaml.emit(j) ++ "\n\n" 24 case O.FToon{}: 25 Toon.emit(j) ++ "\n\n" 26 case _: 27 Json.emit.go(j, 0, True{}, 0n) ++ "\n" 28 29 def parse_req(f: O.Fmt, +msg: String) -> Result<&2, &2, String, Json.Json>: 30 match f: 31 case O.FYaml{}: 32 Yaml.parse(msg) 33 case O.FToon{}: 34 Toon.parse(msg) 35 case _: 36 Json.parse(msg) 37 38 def run_req(+f: O.Fmt, r: M.Req) -> IO(String): 39 match r: 40 case M.ReqOk{id, argv}: 41 IO.bind(Cmd.Out, String, Exec.exec_line(argv, Some{O.FJson{}}), o => IO.pure(String, render(f, M.response(id, o)))) 42 case M.ReqBad{id, code, msg}: 43 IO.pure(String, render(f, M.err_resp(id, code, msg, 2))) 44 45 def run_doc(+f: O.Fmt, r: Result<&2, &2, String, Json.Json>) -> IO(String): 46 match r: 47 case Done{j}: 48 run_req(f, M.plan(j)) 49 case Fail{e}: 50 IO.pure(String, render(f, M.err_resp(Json.JNull{}, "bad_request", e, 2))) 51 52 def handle2(blank: Bool, +f: O.Fmt, +msg: String) -> IO(String): 53 match blank: 54 case True{}: 55 IO.pure(String, "") 56 case False{}: 57 run_doc(f, parse_req(f, msg)) 58 59 # The response text for one message ("" for a blank one). 60 def handle(+f: O.Fmt, +msg: String) -> IO(String): 61 handle2(Fr.blank(msg), f, msg)