1 # share/spin: the spinner share/say's words are drawn with. This is the half 2 # that runs programs: it asks which terminal this process writes to, starts 3 # the mark as a child and stops it. It lives apart from the words because a 4 # program runs through snap's foreign effect, and bend 2.0.32 fails a proof 5 # whose imports reach foreign code, so a law may import share/say but never 6 # this. 7 import Base 8 import 0xead4c7939c9eaca8d47da53b4496b731/main.bend as R 9 import ./say.bend as Say 10 11 # one fd of this process, as a path. A failure is not a terminal. 12 def link(+fd: String) -> IO(String): 13 do IO: 14 +out : String <- R.exec(["sh", "-c", Say.probe(), "sh", fd]) 15 return String.trim(R.text(out)) 16 17 # the terminal this process writes to, stderr's first, or empty for none 18 def dev() -> IO(String): 19 do IO: 20 +err : String <- link("2") 21 +out : String <- link("1") 22 return Say.tty.pick(err, out) 23 24 # the mark, when there is a terminal to draw it on 25 def start(on: Bool, +dev: String, +msg: String) -> IO(Say.Spin): 26 match on: 27 case False{}: 28 IO.pure(Say.Spin, Say.Spin{"", "", ""}) 29 case True{}: 30 do IO: 31 +pid : String <- R.start(["sh", "-c", Say.frames(), "sh", dev, msg]) 32 return Say.Spin{dev, String.trim(pid), msg} 33 34 def hold.at(plain: Bool, +dev: String, +msg: String) -> IO(Say.Spin): 35 match plain: 36 case True{}: 37 do IO: 38 IO.print_err(msg) 39 return Say.Spin{"", "", ""} 40 case False{}: 41 start(True{}, dev, msg) 42 43 # the line, then the mark when a terminal will show it moving. A pipe gets 44 # the line and nothing else. 45 def hold(+msg: String) -> IO(Say.Spin): 46 do IO: 47 +tty : String <- dev() 48 hold.at(String.is_empty(tty), tty, msg) 49 50 # a spinner that never started still owes its line, unless the plain path 51 # already printed it and left the message empty 52 def owed(empty: Bool, +msg: String) -> IO(Unit): 53 match empty: 54 case True{}: 55 IO.pure(Unit, Unit{}) 56 case False{}: 57 IO.print_err(msg) 58 59 # the mark stopped, and the line written where it was 60 def done.live(+dev: String, +pid: String, +msg: String) -> IO(Unit): 61 do IO: 62 _k : String <- R.exec(["kill", pid]) 63 _s : String <- R.exec(["sh", "-c", Say.settle(), "sh", dev, msg]) 64 return Unit{} 65 66 def done.bad(bad: Bool, +dev: String, +pid: String, +msg: String) -> IO(Unit): 67 match bad: 68 case True{}: 69 owed(String.is_empty(msg), msg) 70 case False{}: 71 done.live(dev, pid, msg) 72 73 # the step finished: the mark stopped and the line left, or the line owed 74 def done(spin: Say.Spin) -> IO(Unit): 75 Say.Spin{+term, +pid, +msg} = spin 76 done.bad(Bool.or(String.is_empty(pid), String.eq(pid, "0")), term, pid, msg)