clibase.bend

cswap-bend-cli@0.1.0.0 · 95 lines · 19 definitions

1import Base
2import cswap-bend-cli@0.1.0.0/usage.bend as Usage
3import cswap-bend-cli@0.1.0.0/clock.bend as Clock
4import cswap-bend-cli@0.1.0.0/fs.bend as Fs
5import cswap-bend-cli@0.1.0.0/act.bend as Act
6import cswap-bend-cli@0.1.0.0/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
11def version() -> String:
12  "cswap-bend 0.1.0"
13
14def ActM(-A: Type) -> Type:
15  Act.of(A)
16
17def ActM.pure(-A: Type, x: A) -> Act.of(A):
18  Act.pure(A, x)
19
20def 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
23def 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.
31def 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
34def fail_out(+json: Bool, ce: U32 & String) -> Cmd.Out:
35  (c, m) = ce
36  Cmd.failed(json, kind_of(c), m)
37
38def 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
45def 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
48def done(o: Cmd.Out) -> Act.of(Cmd.Out):
49  Act.pure(Cmd.Out, o)
50
51def say(+text: String) -> Act.of(Cmd.Out):
52  Act.pure(Cmd.Out, Cmd.ok(text))
53
54def first_line(ls: List<&2, String>) -> String:
55  match ls:
56    case Con{h, t}:
57      h
58    case Nil{}:
59      ""
60
61def 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).
69def 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
72def 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
75def text_out(+t: String) -> Cmd.Out:
76  Cmd.ok(t)
77
78def affine(ts: List<&2, String>) -> List<String>:
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.
87type Tm is Data:
88  Tm{now: U32, off: U32, tick: Nat}
89
90def times() -> Act.of(Tm):
91  do ActM<Tm>:
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}