forked from
mary.my.id/atcute
a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky
1interface Node<T> {
2 value: T;
3 next: Node<T> | undefined;
4}
5
6/** a stack data structure (lifo) */
7class Stack<T> implements Iterable<T> {
8 #head: Node<T> | undefined;
9 #tail: Node<T> | undefined;
10 #size: number = 0;
11
12 /** size of the stack */
13 get size(): number {
14 return this.#size;
15 }
16
17 /**
18 * clear the stack
19 */
20 clear(): void {
21 this.#head = undefined;
22 this.#tail = undefined;
23 this.#size = 0;
24 }
25
26 /**
27 * adds a value to the top of the stack
28 * @param value value to add
29 * @returns the stack instance
30 */
31 push(value: T): this {
32 const node: Node<T> = { value, next: this.#head };
33
34 if (this.#head === undefined) {
35 this.#tail = node;
36 }
37
38 this.#head = node;
39 this.#size++;
40 return this;
41 }
42
43 /**
44 * removes the top value from the stack
45 * @returns last added value, or undefined if empty
46 */
47 pop(): T | undefined {
48 const head = this.#head;
49 if (head === undefined) {
50 return;
51 }
52
53 this.#head = head.next;
54 this.#size--;
55
56 if (this.#head === undefined) {
57 this.#tail = undefined;
58 }
59
60 return head.value;
61 }
62
63 /**
64 * get the top value without removing from stack
65 * @returns last added value, or undefined if empty
66 */
67 peek(): T | undefined {
68 return this.#head?.value;
69 }
70
71 /**
72 * get the bottom value without removing from stack
73 * @returns first added value, or undefined if empty
74 */
75 peekBottom(): T | undefined {
76 return this.#tail?.value;
77 }
78
79 /**
80 * returns an iterator that drains all the values from stack
81 */
82 drain(): IterableIterator<T, undefined, undefined> {
83 // oxlint-disable-next-line no-this-alias
84 const self = this;
85
86 return {
87 next() {
88 const head = self.#head;
89 if (head === undefined) {
90 return { done: true, value: undefined };
91 }
92
93 self.#head = head.next;
94 self.#size--;
95
96 if (self.#head === undefined) {
97 self.#tail = undefined;
98 }
99
100 return { done: false, value: head.value };
101 },
102 [Symbol.iterator]() {
103 return this;
104 },
105 };
106 }
107
108 /**
109 * iterates over the stack without draining
110 */
111 [Symbol.iterator](): Iterator<T, undefined, undefined> {
112 let current = this.#head;
113
114 return {
115 next() {
116 if (current === undefined) {
117 return { done: true, value: undefined };
118 }
119
120 const value = current.value;
121 current = current.next;
122
123 return { done: false, value: value };
124 },
125 };
126 }
127}
128
129export default Stack;