@atproto/repo
Version:
atproto repo and MST implementation
119 lines (107 loc) • 2.97 kB
text/typescript
import { MST, NodeEntry } from './mst'
type WalkerStatusDone = {
done: true
}
type WalkerStatusProgress = {
done: false
curr: NodeEntry
walking: MST | null // walking set to null if `curr` is the root of the tree
index: number
}
type WalkerStatus = WalkerStatusDone | WalkerStatusProgress
export class MstWalker {
stack: WalkerStatus[] = []
status: WalkerStatus
constructor(public root: MST) {
this.status = {
done: false,
curr: root,
walking: null,
index: 0,
}
}
// return the current layer of the node you are walking
layer(): number {
if (this.status.done) {
throw new Error('Walk is done')
}
if (this.status.walking) {
return this.status.walking.layer ?? 0
}
// if curr is the root of the tree, add 1
if (this.status.curr.isTree()) {
return (this.status.curr.layer ?? 0) + 1
}
throw new Error('Could not identify layer of walk')
}
// move to the next node in the subtree, skipping over the subtree
async stepOver(): Promise<void> {
if (this.status.done) return
// if stepping over the root of the node, we're done
if (this.status.walking === null) {
this.status = { done: true }
return
}
const entries = await this.status.walking.getEntries()
this.status.index++
const next = entries[this.status.index]
if (!next) {
const popped = this.stack.pop()
if (!popped) {
this.status = { done: true }
return
} else {
this.status = popped
await this.stepOver()
return
}
} else {
this.status.curr = next
}
}
// step into a subtree, throws if currently pointed at a leaf
async stepInto(): Promise<void> {
if (this.status.done) return
// edge case for very start of walk
if (this.status.walking === null) {
if (!this.status.curr.isTree()) {
throw new Error('The root of the tree cannot be a leaf')
}
const next = await this.status.curr.atIndex(0)
if (!next) {
this.status = { done: true }
} else {
this.status = {
done: false,
walking: this.status.curr,
curr: next,
index: 0,
}
}
return
}
if (!this.status.curr.isTree()) {
throw new Error('No tree at pointer, cannot step into')
}
const next = await this.status.curr.atIndex(0)
if (!next) {
throw new Error(
'Tried to step into a node with 0 entries which is invalid',
)
}
this.stack.push({ ...this.status })
this.status.walking = this.status.curr
this.status.curr = next
this.status.index = 0
}
// advance the pointer to the next node in the tree,
// stepping into the current node if necessary
async advance(): Promise<void> {
if (this.status.done) return
if (this.status.curr.isLeaf()) {
await this.stepOver()
} else {
await this.stepInto()
}
}
}