porting all github actions from bluesky-social/indigo to tangled CI
at main 3.8 kB view raw
1package mst 2 3import ( 4 "context" 5 "fmt" 6 7 "github.com/bluesky-social/indigo/util" 8 cid "github.com/ipfs/go-cid" 9 cbor "github.com/ipfs/go-ipld-cbor" 10) 11 12type DiffOp struct { 13 Depth int 14 Op string 15 Rpath string 16 OldCid cid.Cid 17 NewCid cid.Cid 18} 19 20// TODO: this code isn't great, should be rewritten on top of the baseline datastructures once functional and correct 21func DiffTrees(ctx context.Context, bs cbor.IpldBlockstore, from, to cid.Cid) ([]*DiffOp, error) { 22 cst := util.CborStore(bs) 23 24 if from == cid.Undef { 25 return identityDiff(ctx, bs, to) 26 } 27 28 ft := LoadMST(cst, from) 29 tt := LoadMST(cst, to) 30 31 fents, err := ft.getEntries(ctx) 32 if err != nil { 33 return nil, err 34 } 35 36 tents, err := tt.getEntries(ctx) 37 if err != nil { 38 return nil, err 39 } 40 41 var ixf, ixt int 42 var out []*DiffOp 43 for ixf < len(fents) && ixt < len(tents) { 44 ef := fents[ixf] 45 et := tents[ixt] 46 47 if nodeEntriesEqual(&ef, &et) { 48 ixf++ 49 ixt++ 50 continue 51 } 52 53 if ef.isLeaf() && et.isLeaf() { 54 if ef.Key == et.Key { 55 if ef.Val == et.Val { 56 return nil, fmt.Errorf("hang on, why are these leaves equal?") 57 } 58 59 out = append(out, &DiffOp{ 60 Op: "mut", 61 Rpath: ef.Key, 62 OldCid: ef.Val, 63 NewCid: et.Val, 64 }) 65 ixf++ 66 ixt++ 67 continue 68 } 69 // different keys... what do? 70 71 // if the 'to' key is earlier than the 'from' key, call it an insert 72 // otherwise call it a deletion? 73 74 if ef.Key > et.Key { 75 out = append(out, &DiffOp{ 76 Op: "add", 77 Rpath: et.Key, 78 NewCid: et.Val, 79 }) 80 // only walk forward the pointer that was 'behind' 81 ixt++ 82 } else { 83 out = append(out, &DiffOp{ 84 Op: "del", 85 Rpath: ef.Key, 86 OldCid: ef.Val, 87 }) 88 // only walk forward the pointer that was 'behind' 89 ixf++ 90 } 91 92 // call it an insertion? 93 continue 94 } 95 96 if ef.isTree() { 97 sub, err := ef.Tree.getEntries(ctx) 98 if err != nil { 99 return nil, err 100 } 101 102 fents = append(sub, fents[ixf+1:]...) 103 ixf = 0 104 continue 105 } 106 107 if et.isTree() { 108 sub, err := et.Tree.getEntries(ctx) 109 if err != nil { 110 return nil, err 111 } 112 113 tents = append(sub, tents[ixt+1:]...) 114 ixt = 0 115 continue 116 } 117 } 118 119 for ; ixf < len(fents); ixf++ { 120 // deletions 121 122 e := fents[ixf] 123 if e.isLeaf() { 124 out = append(out, &DiffOp{ 125 Op: "del", 126 Rpath: e.Key, 127 OldCid: e.Val, 128 }) 129 130 } else if e.isTree() { 131 if err := e.Tree.WalkLeavesFrom(ctx, "", func(key string, val cid.Cid) error { 132 out = append(out, &DiffOp{ 133 Op: "del", 134 Rpath: key, 135 OldCid: val, 136 }) 137 return nil 138 }); err != nil { 139 return nil, err 140 } 141 } 142 } 143 144 for ; ixt < len(tents); ixt++ { 145 // insertions 146 147 e := tents[ixt] 148 if e.isLeaf() { 149 out = append(out, &DiffOp{ 150 Op: "add", 151 Rpath: e.Key, 152 NewCid: e.Val, 153 }) 154 155 } else if e.isTree() { 156 if err := e.Tree.WalkLeavesFrom(ctx, "", func(key string, val cid.Cid) error { 157 out = append(out, &DiffOp{ 158 Op: "add", 159 Rpath: key, 160 NewCid: val, 161 }) 162 return nil 163 }); err != nil { 164 return nil, err 165 } 166 } 167 } 168 169 return out, nil 170} 171 172func nodeEntriesEqual(a, b *nodeEntry) bool { 173 if !(a.Key == b.Key && a.Val == b.Val) { 174 return false 175 } 176 177 if a.Tree == nil && b.Tree == nil { 178 return true 179 } 180 181 if a.Tree != nil && b.Tree != nil && a.Tree.pointer == b.Tree.pointer { 182 return true 183 } 184 185 return false 186} 187 188func identityDiff(ctx context.Context, bs cbor.IpldBlockstore, root cid.Cid) ([]*DiffOp, error) { 189 cst := util.CborStore(bs) 190 tt := LoadMST(cst, root) 191 192 var ops []*DiffOp 193 if err := tt.WalkLeavesFrom(ctx, "", func(key string, val cid.Cid) error { 194 ops = append(ops, &DiffOp{ 195 Op: "add", 196 Rpath: key, 197 NewCid: val, 198 }) 199 return nil 200 }); err != nil { 201 return nil, err 202 } 203 return ops, nil 204}