1use patterns_macros::because;
2
3pub fn either<T>(when: bool, yes: T, no: T) -> T {
4 if when {
5 yes
6 } else {
7 no
8 }
9}
10
11pub fn is_below<T: PartialOrd>(a: T, b: T) -> bool {
12 a < b
13}
14
15pub fn is_above<T: PartialOrd>(a: T, b: T) -> bool {
16 a > b
17}
18
19pub fn is_at_most<T: PartialOrd>(a: T, b: T) -> bool {
20 a <= b
21}
22
23pub fn is_at_least<T: PartialOrd>(a: T, b: T) -> bool {
24 a >= b
25}
26
27pub fn is_same<T: PartialEq>(a: T, b: T) -> bool {
28 a == b
29}
30because!(either, "the replacement for an if expression in a binding, so a choice between two values is a call and the branch cannot grow a body");
31because!(is_below, "a strict ordering test named so a comparison operator never appears in spec or app");
32because!(is_above, "the mirror of is_below, named rather than derived because negating a strict comparison flips to a non-strict one and that is a common error");
33because!(is_at_most, "the non-strict form of is_below, separate because the boundary case is exactly what a limit turns on");
34because!(is_at_least, "the non-strict form of is_above, separate because the boundary case is exactly what a limit turns on");
35because!(is_same, "equality as a call, so an equality operator never appears in spec or app");
36
37pub fn either_lazily<T>(when: bool, yes: fn() -> T, no: fn() -> T) -> T {
38 if when {
39 yes()
40 } else {
41 no()
42 }
43}
44because!(either_lazily, "the form that builds only the branch it returns, for a choice whose arms are expensive or whose unused arm would panic; either is eager and evaluates both arguments before the call");
45
46pub fn refuse_when<E, F: Clone>(checks: &[(bool, F)], ok: E) -> Result<E, F> {
47 for (refused, fault) in checks {
48 if *refused {
49 return Err(fault.clone());
50 }
51 }
52 Ok(ok)
53}
54because!(refuse_when, "a table of guards read top to bottom, the first true one refusing with its fault, so a decide arm with several rules is a list in the order the rules apply rather than a nest of either calls that reads inside out; every guard is built before the call, and refuse_when_lazily is the form for a guard that must not run until an earlier one has passed");
55
56pub type Guard<S, C, F> = (fn(&S, &C) -> bool, F);
57
58pub fn refuse_when_lazily<S, C, E, F: Clone>(
59 state: &S,
60 command: &C,
61 checks: &[Guard<S, C, F>],
62 ok: E,
63) -> Result<E, F> {
64 for (refused, fault) in checks {
65 if refused(state, command) {
66 return Err(fault.clone());
67 }
68 }
69 Ok(ok)
70}
71because!(refuse_when_lazily, "the guard table of refuse_when with each guard a function of the state and the command, run only until the first refusal, kept beside refuse_when because an eager table builds every guard before the call and a guard that is only safe to evaluate after an earlier one has refused cannot be written that way");
72
73pub fn flipped(value: bool) -> bool {
74 !value
75}
76because!(flipped, "a flag turned over, which a binding cannot write because the negation operator is control flow to the checker, and which the first setting that toggles needs before anything else in a project works");