Browse Source

Improve abstraction and controllability of tree-walking methods

master
buttercat1791 10 months ago
parent
commit
1e450dd4b7
  1. 118
      src/lib/data_structures/publication_tree.ts

118
src/lib/data_structures/publication_tree.ts

@ -12,6 +12,16 @@ enum PublicationTreeNodeStatus {
Error, Error,
} }
export enum TreeTraversalMode {
Leaves,
All,
}
enum TreeTraversalDirection {
Forward,
Backward,
}
interface PublicationTreeNode { interface PublicationTreeNode {
type: PublicationTreeNodeType; type: PublicationTreeNodeType;
status: PublicationTreeNodeStatus; status: PublicationTreeNodeStatus;
@ -344,42 +354,39 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
return this; return this;
} }
// TODO: Add `previous()` method. /**
* Return the next event in the tree for the given traversal mode.
async next(): Promise<IteratorResult<NDKEvent | null>> { *
* @param mode The traversal mode. Can be {@link TreeTraversalMode.Leaves} or
* {@link TreeTraversalMode.All}.
* @returns The next event in the tree, or null if the tree is empty.
*/
async next(
mode: TreeTraversalMode = TreeTraversalMode.Leaves
): Promise<IteratorResult<NDKEvent | null>> {
if (!this.#cursor.target) { if (!this.#cursor.target) {
if (await this.#cursor.tryMoveTo(this.#bookmark)) { if (await this.#cursor.tryMoveTo(this.#bookmark)) {
const event = await this.getEvent(this.#cursor.target!.address); return this.#yieldEventAtCursor(false);
return { done: false, value: event };
} }
} }
// Based on Raymond Chen's tree traversal algorithm example. if (mode === TreeTraversalMode.Leaves) {
// https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300 return this.#walkLeaves(TreeTraversalDirection.Forward);
do {
if (await this.#cursor.tryMoveToNextSibling()) {
while (await this.#cursor.tryMoveToFirstChild()) {
continue;
}
if (this.#cursor.target!.status === PublicationTreeNodeStatus.Error) {
return { done: false, value: null };
}
const event = await this.getEvent(this.#cursor.target!.address);
return { done: false, value: event };
}
} while (this.#cursor.tryMoveToParent());
if (this.#cursor.target!.status === PublicationTreeNodeStatus.Error) {
return { done: false, value: null };
} }
// If we get to this point, we're at the root node (can't move up any more). return this.#preorderWalkAll(TreeTraversalDirection.Forward);
return { done: true, value: null };
} }
async previous(): Promise<IteratorResult<NDKEvent | null>> { /**
* Return the previous event in the tree for the given traversal mode.
*
* @param mode The traversal mode. Can be {@link TreeTraversalMode.Leaves} or
* {@link TreeTraversalMode.All}.
* @returns The previous event in the tree, or null if the tree is empty.
*/
async previous(
mode: TreeTraversalMode = TreeTraversalMode.Leaves
): Promise<IteratorResult<NDKEvent | null>> {
if (!this.#cursor.target) { if (!this.#cursor.target) {
if (await this.#cursor.tryMoveTo(this.#bookmark)) { if (await this.#cursor.tryMoveTo(this.#bookmark)) {
const event = await this.getEvent(this.#cursor.target!.address); const event = await this.getEvent(this.#cursor.target!.address);
@ -387,11 +394,40 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
} }
} }
// Based on Raymond Chen's tree traversal algorithm example. if (mode === TreeTraversalMode.Leaves) {
// https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300 return this.#walkLeaves(TreeTraversalDirection.Backward);
}
return this.#preorderWalkAll(TreeTraversalDirection.Backward);
}
async #yieldEventAtCursor(done: boolean): Promise<IteratorResult<NDKEvent | null>> {
const value = (await this.getEvent(this.#cursor.target!.address)) ?? null;
return { done, value };
}
/**
* Walks the tree in the given direction, yielding the event at each leaf.
*
* @param direction The direction to walk the tree.
* @returns The event at the leaf, or null if the tree is empty.
*
* Based on Raymond Chen's tree traversal algorithm example.
* https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300
*/
async #walkLeaves(
direction: TreeTraversalDirection = TreeTraversalDirection.Forward
): Promise<IteratorResult<NDKEvent | null>> {
const tryMoveToSibling: () => Promise<boolean> = direction === TreeTraversalDirection.Forward
? this.#cursor.tryMoveToNextSibling.bind(this.#cursor)
: this.#cursor.tryMoveToPreviousSibling.bind(this.#cursor);
const tryMoveToChild: () => Promise<boolean> = direction === TreeTraversalDirection.Forward
? this.#cursor.tryMoveToFirstChild.bind(this.#cursor)
: this.#cursor.tryMoveToLastChild.bind(this.#cursor);
do { do {
if (await this.#cursor.tryMoveToPreviousSibling()) { if (await tryMoveToSibling()) {
while (await this.#cursor.tryMoveToLastChild()) { while (await tryMoveToChild()) {
continue; continue;
} }
@ -399,8 +435,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
return { done: false, value: null }; return { done: false, value: null };
} }
const event = await this.getEvent(this.#cursor.target!.address); return this.#yieldEventAtCursor(false);
return { done: false, value: event };
} }
} while (this.#cursor.tryMoveToParent()); } while (this.#cursor.tryMoveToParent());
@ -408,9 +443,26 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
return { done: false, value: null }; return { done: false, value: null };
} }
// If we get to this point, we're at the root node (can't move up any more).
return { done: true, value: null }; return { done: true, value: null };
} }
/**
* Walks the tree in the given direction, yielding the event at each node.
*
* @param direction The direction to walk the tree.
* @returns The event at the node, or null if the tree is empty.
*
* Based on Raymond Chen's preorder walk algorithm example.
* https://devblogs.microsoft.com/oldnewthing/20200107-00/?p=103304
*/
async #preorderWalkAll(
direction: TreeTraversalDirection = TreeTraversalDirection.Forward
): Promise<IteratorResult<NDKEvent | null>> {
// TODO: Implement this.
return { done: false, value: null };
}
// #endregion // #endregion
// #region Private Methods // #region Private Methods

Loading…
Cancel
Save