1 import Base 2 import ./type.bend as D 3 4 # ---- what a deploy breaks: compared with what was deployed before (its address and stores) ---- 5 def address(d: D.Deployment) -> String: 6 match d: 7 case D.Deployment{a, _, _, _}: 8 a 9 10 def stores(d: D.Deployment) -> List<&2, String>: 11 match d: 12 case D.Deployment{_, ss, _, _}: 13 ss 14 15 def accepted(d: D.Deployment) -> List<&2, D.Break>: 16 match d: 17 case D.Deployment{_, _, acc, _}: 18 acc 19 20 def keep_break(present: Bool, +o: String, rest: List<&2, D.Break>) -> List<&2, D.Break>: 21 match present: 22 case True{}: 23 rest 24 case False{}: 25 D.StoreRemoved{o} <> rest 26 27 def removed(olds: List<&2, String>, +news: List<&2, String>) -> List<&2, D.Break>: 28 match olds: 29 case []: 30 [] 31 case +o <> rest: 32 keep_break(List.contains(~String, ~String.eq, news, o), o, removed(rest, news)) 33 34 # The address changing, then each store the new deployment drops. A first deploy (no address before) breaks nothing. 35 def breaks(+before: String, olds: List<&2, String>, +d: D.Deployment) -> List<&2, D.Break>: 36 List.append(&2, D.Break, 37 Bool.pick(List<&2, D.Break>, String.is_empty(before) || String.eq(before, address(d)), [], [D.AddressChanged{}]), 38 removed(olds, stores(d))) 39 40 def break_eq(a: D.Break, b: D.Break) -> Bool: 41 match a b: 42 case D.AddressChanged{} D.AddressChanged{}: 43 True{} 44 case D.StoreRemoved{x} D.StoreRemoved{y}: 45 String.eq(x, y) 46 case _ _: 47 False{} 48 49 def breaks_eq(a: List<&2, D.Break>, b: List<&2, D.Break>) -> Bool: 50 match a: 51 case []: 52 List.is_empty(&2, D.Break, b) 53 case x <> xs: 54 match b: 55 case []: 56 False{} 57 case y <> ys: 58 break_eq(x, y) && breaks_eq(xs, ys) 59 60 def show_break(b: D.Break) -> String: 61 match b: 62 case D.AddressChanged{}: 63 "the address changes" 64 case D.StoreRemoved{n}: 65 "store " ++ n ++ " is removed" 66 67 def shown(bs: List<&2, D.Break>) -> List<&2, String>: 68 match bs: 69 case []: 70 [] 71 case b <> rest: 72 show_break(b) <> shown(rest) 73 74 def show_breaks(bs: List<&2, D.Break>) -> String: 75 String.join(shown(bs), ", ") 76 77 def store_lines(ss: List<&2, String>) -> String: 78 match ss: 79 case []: 80 "" 81 case s <> rest: 82 "store " ++ s ++ "\n" ++ store_lines(rest) 83 84 # What a deploy records as deployed: the address, then one store per line. 85 def record(+d: D.Deployment) -> String: 86 "address " ++ address(d) ++ "\n" ++ store_lines(stores(d)) 87 88 def keep_rest(c: Bool, +prefix: String, l: String, rest: List<&2, String>) -> List<&2, String>: 89 match c: 90 case True{}: 91 String.drop(l, String.length(prefix)) <> rest 92 case False{}: 93 rest 94 95 # The lines that start with a prefix, without it. 96 def after(+prefix: String, ls: List<&2, String>) -> List<&2, String>: 97 match ls: 98 case []: 99 [] 100 case +l <> rest: 101 keep_rest(String.starts_with(l, prefix), prefix, l, after(prefix, rest)) 102 103 # The address and stores in a record ("" and [] when nothing was deployed). 104 def recorded_address(+text: String) -> String: 105 Maybe.default(&2, String, List.head(&2, String, after("address ", String.lines(text))), "") 106 107 def recorded_stores(+text: String) -> List<&2, String>: 108 after("store ", String.lines(text))