1 import Base 2 import ./usage.bend as Usage 3 import ./clock.bend as Clock 4 import ./fs.bend as Fs 5 import ./act.bend as Act 6 import ./cmd.bend as Cmd 7 8 # The cswap command line (cli.py): parse, dispatch, exit codes and output. 9 # Every command answers a Cmd.Out; main prints it and exits with its code. 10 11 def version() -> String: 12 "cswap-bend 0.1.0" 13 14 def ActM(-A: Type) -> Type: 15 Act.of(A) 16 17 def ActM.pure(-A: Type, x: A) -> Act.of(A): 18 Act.pure(A, x) 19 20 def ActM.bind(-A: Type, -B: Type, m: Act.of(A), f: A -> Act.of(B)) -> Act.of(B): 21 Act.bind(A, B, m, f) 22 23 def sel.o(c: Bool, a: Cmd.Out, b: Cmd.Out) -> Cmd.Out: 24 match c: 25 case True{}: 26 a 27 case False{}: 28 b 29 30 # The exception class the reference raises for a store failure code. 31 def kind_of(+code: U32) -> String: 32 Clock.sel.s(U32.is_eq(code, 3), "AccountNotFoundError", Clock.sel.s(U32.is_eq(code, 2), "ValidationError", "ConfigError")) 33 34 def fail_out(+json: Bool, ce: U32 & String) -> Cmd.Out: 35 (c, m) = ce 36 Cmd.failed(json, kind_of(c), m) 37 38 def act_out(+json: Bool, r: Fs.Res(Cmd.Out)) -> Cmd.Out: 39 match r: 40 case Fail{e}: 41 fail_out(json, e) 42 case Done{o}: 43 o 44 45 def run_act(+json: Bool, a: Act.of(Cmd.Out)) -> IO(Cmd.Out): 46 IO.bind(Fs.Res(Cmd.Out), Cmd.Out, a, r => IO.pure(Cmd.Out, act_out(json, r))) 47 48 def done(o: Cmd.Out) -> Act.of(Cmd.Out): 49 Act.pure(Cmd.Out, o) 50 51 def say(+text: String) -> Act.of(Cmd.Out): 52 Act.pure(Cmd.Out, Cmd.ok(text)) 53 54 def first_line(ls: List<&2, String>) -> String: 55 match ls: 56 case Con{h, t}: 57 h 58 case Nil{}: 59 "" 60 61 def stdin_line2(r: Fs.Res(String)) -> Fs.Res(String): 62 match r: 63 case Done{t}: 64 Done{first_line(String.lines(t))} 65 case Fail{e}: 66 Done{""} 67 68 # One line from standard input ("" at end of input). 69 def read_line() -> Act.of(String): 70 IO.bind(Fs.Res(String), Fs.Res(String), Fs.read_stdin(), r => IO.pure(Fs.Res(String), stdin_line2(r))) 71 72 def is_yes(+a: String) -> Bool: 73 String.eq(String.to_lower(String.trim(a)), "y") || String.eq(String.to_lower(String.trim(a)), "yes") 74 75 def text_out(+t: String) -> Cmd.Out: 76 Cmd.ok(t) 77 78 def affine(ts: List<&2, String>) -> List: 79 match ts: 80 case Nil{}: 81 [] 82 case Con{h, t}: 83 h <> affine(t) 84 85 # The clock and the local UTC offset (seconds), and a tie-breaker for claim ids, 86 # read once at the start of a command. 87 type Tm is Data: 88 Tm{now: U32, off: U32, tick: Nat} 89 90 def times() -> Act.of(Tm): 91 do ActM: 92 now : U32 <- Act.lift(U32, Clock.now_s()) 93 off : U32 <- Act.lift(U32, Usage.local_offset()) 94 tick : Nat <- Act.lift(Nat, IO.now()) 95 return Tm{now, off, tick}