src/share/spin.bend source
src/share/spin.bend on the hub · documented module
# share/spin: the spinner share/say's words are drawn with. This is the half# that runs programs: it asks which terminal this process writes to, starts# the mark as a child and stops it. It lives apart from the words because a# program runs through snap's foreign effect, and bend 2.0.32 fails a proof# whose imports reach foreign code, so a law may import share/say but never# this.import Baseimport 0xabe575924687afad4cee1a2c1194d639/main.bend as Rimport ./say.bend as Say# one fd of this process, as a path. A failure is not a terminal.def link(+fd: String) -> IO(String): do IO<String>: +out : String <- R.exec(["sh", "-c", Say.probe(), "sh", fd]) return String.trim(R.text(out))# the terminal this process writes to, stderr's first, or empty for nonedef dev() -> IO(String): do IO<String>: +err : String <- link("2") +out : String <- link("1") return Say.tty.pick(err, out)# the mark, when there is a terminal to draw it ondef start(on: Bool, +dev: String, +msg: String) -> IO(Say.Spin): match on: case False{}: IO.pure(Say.Spin, Say.Spin{"", "", ""}) case True{}: do IO<Say.Spin>: +pid : String <- R.start(["sh", "-c", Say.frames(), "sh", dev, msg]) return Say.Spin{dev, String.trim(pid), msg}def hold.at(plain: Bool, +dev: String, +msg: String) -> IO(Say.Spin): match plain: case True{}: do IO<Say.Spin>: IO.print_err(msg) return Say.Spin{"", "", ""} case False{}: start(True{}, dev, msg)# the line, then the mark when a terminal will show it moving. A pipe gets# the line and nothing else.def hold(+msg: String) -> IO(Say.Spin): do IO<Say.Spin>: +tty : String <- dev() hold.at(String.is_empty(tty), tty, msg)# a spinner that never started still owes its line, unless the plain path# already printed it and left the message emptydef owed(empty: Bool, +msg: String) -> IO(Unit): match empty: case True{}: IO.pure(Unit, Unit{}) case False{}: IO.print_err(msg)# the mark stopped, and the line written where it wasdef done.live(+dev: String, +pid: String, +msg: String) -> IO(Unit): do IO<Unit>: _k : String <- R.exec(["kill", pid]) _s : String <- R.exec(["sh", "-c", Say.settle(), "sh", dev, msg]) return Unit{}def done.bad(bad: Bool, +dev: String, +pid: String, +msg: String) -> IO(Unit): match bad: case True{}: owed(String.is_empty(msg), msg) case False{}: done.live(dev, pid, msg)# the step finished: the mark stopped and the line left, or the line oweddef done(spin: Say.Spin) -> IO(Unit): Say.Spin{+term, +pid, +msg} = spin done.bad(Bool.or(String.is_empty(pid), String.eq(pid, "0")), term, pid, msg)