Browse Source

Improve abstraction and controllability of tree-walking methods

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

112
src/lib/data_structures/publication_tree.ts

@ -12,6 +12,16 @@ enum PublicationTreeNodeStatus { @@ -12,6 +12,16 @@ enum PublicationTreeNodeStatus {
Error,
}
export enum TreeTraversalMode {
Leaves,
All,
}
enum TreeTraversalDirection {
Forward,
Backward,
}
interface PublicationTreeNode {
type: PublicationTreeNodeType;
status: PublicationTreeNodeStatus;
@ -344,54 +354,80 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -344,54 +354,80 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
return this;
}
// TODO: Add `previous()` method.
async next(): Promise<IteratorResult<NDKEvent | null>> {
/**
* Return the next 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 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 (await this.#cursor.tryMoveTo(this.#bookmark)) {
const event = await this.getEvent(this.#cursor.target!.address);
return { done: false, value: event };
return this.#yieldEventAtCursor(false);
}
}
// Based on Raymond Chen's tree traversal algorithm example.
// https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300
do {
if (await this.#cursor.tryMoveToNextSibling()) {
while (await this.#cursor.tryMoveToFirstChild()) {
continue;
if (mode === TreeTraversalMode.Leaves) {
return this.#walkLeaves(TreeTraversalDirection.Forward);
}
if (this.#cursor.target!.status === PublicationTreeNodeStatus.Error) {
return { done: false, value: null };
return this.#preorderWalkAll(TreeTraversalDirection.Forward);
}
/**
* 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 (await this.#cursor.tryMoveTo(this.#bookmark)) {
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 { done: true, value: null };
if (mode === TreeTraversalMode.Leaves) {
return this.#walkLeaves(TreeTraversalDirection.Backward);
}
async previous(): Promise<IteratorResult<NDKEvent | null>> {
if (!this.#cursor.target) {
if (await this.#cursor.tryMoveTo(this.#bookmark)) {
const event = await this.getEvent(this.#cursor.target!.address);
return { done: false, value: event };
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 };
}
// Based on Raymond Chen's tree traversal algorithm example.
// https://devblogs.microsoft.com/oldnewthing/20200106-00/?p=103300
/**
* 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 {
if (await this.#cursor.tryMoveToPreviousSibling()) {
while (await this.#cursor.tryMoveToLastChild()) {
if (await tryMoveToSibling()) {
while (await tryMoveToChild()) {
continue;
}
@ -399,8 +435,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -399,8 +435,7 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> {
return { done: false, value: null };
}
const event = await this.getEvent(this.#cursor.target!.address);
return { done: false, value: event };
return this.#yieldEventAtCursor(false);
}
} while (this.#cursor.tryMoveToParent());
@ -408,9 +443,26 @@ export class PublicationTree implements AsyncIterable<NDKEvent | null> { @@ -408,9 +443,26 @@ export class PublicationTree implements AsyncIterable<NDKEvent | 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 };
}
/**
* 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
// #region Private Methods

Loading…
Cancel
Save