~/bend-docscommunity

src/ez/quiet.bend source

src/ez/quiet.bend on the hub · documented module

# ez/quiet: bend's own chatter dropped from a run's output, so a test sees only# what its program printed. Since 2.0.32 bend prints no check report in front# of a program it runs: a verdict (`ALL PROOFS CHECK` or `SOME PROOFS FAIL`)# is printed only for a file with no main, and a run that fails its check# fails the test on what it printed, which is kept. The report lines older# releases printed in front of every run are no longer dropped.## bend also nags about its own updates: `bend 2.0.21 is available: run bend# update`, printed whenever a newer release exists and the network can be# reached. That line is not the program's output either, and leaving it in# fails every test in the tree the day upstream cuts a release. It is worth# being precise about how bad that is: the gate went from green to 33 of 52# here without a line of ez changing, and it did it the moment a laptop# regained its network, so it is invisible offline and arrives looking like a# regression someone just introduced. It is dropped.import Base# whether this line is bend telling us a newer bend exists. Anchored at both# ends rather than matched loosely: a test that legitimately prints the words# `bend update` in prose should still be compared on them.def is_nag(+line: String) -> Bool:  Bool.and(String.starts_with(line, "bend "),    String.ends_with(line, " is available: run bend update"))# a trailing run of blank lines, being dropped from the right. `live` turns true# at the first line that survives, after which nothing is dropped.type Tail is Data:  Tail{live: Bool, out: List<&2, String>}# a line kept or droppeddef put(drop: Bool, line: String, rest: List<&2, String>) -> List<&2, String>:  match drop:    case True{}:      rest    case False{}:      line <> rest# the lines with the nag droppeddef go(ls: List<&2, String>) -> List<&2, String>:  match ls:    case []:      []    case +h <> t:      put(is_nag(h), h, go(t))# a blank line: kept once something after it has survived, dropped while the# run of trailing blanks is still opendef tail.blank(live: Bool, out: List<&2, String>) -> Tail:  match live:    case True{}:      Tail{True{}, "" <> out}    case False{}:      Tail{False{}, out}# one line offered to the tail. The line is taken apart rather than asked about,# because a fold's step may not declare a `+` parameter to duplicate it.def tail.line(line: String, live: Bool, out: List<&2, String>) -> Tail:  match line:    case SNil{}:      tail.blank(live, out)    case SCon{c, t}:      Tail{True{}, SCon{c, t} <> out}# a fold from the right: until a line survives, a blank one is droppeddef tail.step(line: String, st: Tail) -> Tail:  Tail{live, out} = st  tail.line(line, live, out)# what a finished fold collecteddef tail.out(st: Tail) -> List<&2, String>:  Tail{_live, out} = st  out# the fold from the right, written out. Base's List.foldr takes its step as an# erased argument, and the termination check cannot read through one.def tail.walk(ls: List<&2, String>) -> Tail:  match ls:    case []:      Tail{False{}, []}    case h <> t:      tail.step(h, tail.walk(t))# the lines with the blank ones at the end dropped, the way a shell's `$(...)`# drops a trailing newline, so a comparison never turns on onedef chomp.lines(ls: List<&2, String>) -> List<&2, String>:  tail.out(tail.walk(ls))# the text with its trailing blank lines droppeddef chomp(text: String) -> String:  String.join(chomp.lines(String.lines(text)), "\n")# a run's output as a test's `#|` trailer states it: no update nag, no# trailing newlinedef filter(text: String) -> String:  String.join(chomp.lines(go(String.lines(text))), "\n")