1 import Base 2 import ./fmt/out.bend as O 3 4 # The command line of `cswap rpc [--format json|toon|yaml]` and 5 # `cswap serve --socket PATH [--format json|toon|yaml]`. Pure. 6 7 type Plan is Data: 8 PRpc{fmt: O.Fmt} 9 PServe{path: String, fmt: O.Fmt} 10 PBad{msg: String} 11 12 type A is Data: 13 A{fmt: Maybe<&2, String>, socket: Maybe<&2, String>, err: String, want: String} 14 15 def err(+a: A, msg: String) -> A: 16 match a: 17 case A{fmt, socket, e, w}: 18 A{fmt, socket, Bool.pick(String, String.is_empty(e), msg, e), ""} 19 20 def got(+a: A, +v: String) -> A: 21 match a: 22 case A{fmt, socket, e, w}: 23 Bool.pick(A, String.eq(w, "--format"), A{Some{v}, socket, e, ""}, A{fmt, Some{v}, e, ""}) 24 25 def want(+a: A, +flag: String) -> A: 26 match a: 27 case A{fmt, socket, e, w}: 28 A{fmt, socket, e, flag} 29 30 def pending(+a: A) -> Bool: 31 match a: 32 case A{fmt, socket, e, w}: 33 Bool.not(String.is_empty(w)) 34 35 def sel.a(c: Bool, +x: A, +y: A) -> A: 36 match c: 37 case True{}: 38 x 39 case False{}: 40 y 41 42 def free(+h: String, +a: A) -> A: 43 sel.a(String.eq(h, "--format") || String.eq(h, "--socket"), want(a, h), 44 sel.a(String.starts_with(h, "--format="), got(want(a, "--format"), String.drop(h, 9n)), 45 sel.a(String.starts_with(h, "--socket="), got(want(a, "--socket"), String.drop(h, 9n)), 46 err(a, "unrecognized arguments: " ++ h)))) 47 48 def step(+h: String, +a: A) -> A: 49 sel.a(pending(a), got(a, h), free(h, a)) 50 51 def go(args: List<&2, String>, +a: A) -> A: 52 match args: 53 case Nil{}: 54 a 55 case Con{h, t}: 56 go(t, step(h, a)) 57 58 def end(+a: A) -> A: 59 match a: 60 case A{fmt, socket, e, w}: 61 Bool.pick(A, String.is_empty(w), a, err(a, "argument " ++ w ++ ": expected one argument")) 62 63 def fmt_of2(m: Maybe<&2, O.Fmt>, +name: String) -> Result<&2, &2, String, O.Fmt>: 64 match m: 65 case Some{O.FText{}}: 66 Fail{"argument --format: invalid choice: '" ++ name ++ "' (choose from 'json', 'toon', 'yaml')"} 67 case Some{O.FCsv{}}: 68 Fail{"argument --format: invalid choice: '" ++ name ++ "' (choose from 'json', 'toon', 'yaml')"} 69 case Some{f}: 70 Done{f} 71 case None{}: 72 Fail{"argument --format: invalid choice: '" ++ name ++ "' (choose from 'json', 'toon', 'yaml')"} 73 74 def fmt_of(m: Maybe<&2, String>) -> Result<&2, &2, String, O.Fmt>: 75 match m: 76 case None{}: 77 Done{O.FJson{}} 78 case Some{+n}: 79 fmt_of2(O.of_name(n), n) 80 81 def plan4(serve: Bool, +f: O.Fmt, socket: Maybe<&2, String>) -> Plan: 82 match serve socket: 83 case True{} Some{p}: 84 PServe{p, f} 85 case True{} None{}: 86 PBad{"the following arguments are required: --socket"} 87 case False{} _: 88 PRpc{f} 89 90 def plan3(+verb: String, r: Result<&2, &2, String, O.Fmt>, socket: Maybe<&2, String>) -> Plan: 91 match r: 92 case Fail{m}: 93 PBad{m} 94 case Done{f}: 95 plan4(String.eq(verb, "serve"), f, socket) 96 97 def plan2(+verb: String, +a: A) -> Plan: 98 match a: 99 case A{fmt, socket, e, w}: 100 Bool.pick(Plan, String.is_empty(e), plan3(verb, fmt_of(fmt), socket), PBad{e}) 101 102 # `verb` is rpc or serve; `args` the words after it. 103 def plan(+verb: String, args: List<&2, String>) -> Plan: 104 plan2(verb, end(go(args, A{None{}, None{}, "", ""})))