138 lines
4.3 KiB
TypeScript
138 lines
4.3 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
export class QueueManager<T> {
|
|
private readonly filePath: string;
|
|
private readonly lockFilePath: string;
|
|
private queue: T[];
|
|
private readonly compareFn: (a: T, b: T) => boolean; // Comparison function
|
|
|
|
constructor(filePath: string, compareFn: (a: T, b: T) => boolean) {
|
|
this.filePath = filePath;
|
|
this.lockFilePath = `${filePath}.lock`; // Define the lock file path
|
|
this.queue = [];
|
|
this.compareFn = compareFn;
|
|
|
|
const dir = path.dirname(filePath);
|
|
|
|
// Check if the directory exists, throw error if it doesn't
|
|
if (!fs.existsSync(dir)) {
|
|
throw new Error(`The directory does not exist: ${dir}`);
|
|
}
|
|
|
|
// If the file doesn't exist, create it
|
|
if (!fs.existsSync(filePath)) {
|
|
fs.writeFileSync(filePath, JSON.stringify([], null, 2), 'utf8');
|
|
}
|
|
}
|
|
|
|
// Method to acquire a lock (create .lock file)
|
|
private acquireLock(): void {
|
|
while (fs.existsSync(this.lockFilePath)) {
|
|
// Wait until the lock file is released
|
|
this.sleepSync(100); // 100ms delay before retrying
|
|
}
|
|
// Create the lock file
|
|
fs.writeFileSync(this.lockFilePath, '');
|
|
}
|
|
|
|
// Sleep function to simulate delay for locking mechanism
|
|
private sleepSync(ms: number): void {
|
|
const start = Date.now();
|
|
while (Date.now() - start < ms) {
|
|
// busy wait
|
|
}
|
|
}
|
|
|
|
// Method to release the lock (delete .lock file)
|
|
private releaseLock(): void {
|
|
if (fs.existsSync(this.lockFilePath)) {
|
|
fs.unlinkSync(this.lockFilePath);
|
|
}
|
|
}
|
|
|
|
// Load the queue from the JSON file
|
|
loadQueue(): void {
|
|
this.acquireLock(); // Acquire the lock
|
|
|
|
try {
|
|
const fileData = fs.readFileSync(this.filePath, 'utf8');
|
|
this.queue = JSON.parse(fileData) || [];
|
|
} catch (err) {
|
|
// If the file doesn't exist or is invalid, start with an empty queue
|
|
this.queue = [];
|
|
} finally {
|
|
this.releaseLock(); // Release the lock
|
|
}
|
|
}
|
|
|
|
// Save the queue back to the JSON file
|
|
saveQueue(): void {
|
|
this.acquireLock(); // Acquire the lock
|
|
|
|
try {
|
|
fs.writeFileSync(this.filePath, JSON.stringify(this.queue, null, 2), 'utf8');
|
|
} finally {
|
|
this.releaseLock(); // Release the lock
|
|
}
|
|
}
|
|
|
|
// Enqueue: Add an item to the end of the queue if it doesn't already exist
|
|
enqueue(item: T): void {
|
|
this.loadQueue(); // Ensure we load the latest queue
|
|
|
|
// Check if the item already exists in the queue
|
|
const exists = this.queue.some(existingItem => this.compareFn(existingItem, item));
|
|
|
|
console.log(this.queue);
|
|
|
|
if (!exists) {
|
|
this.queue.push(item);
|
|
this.saveQueue(); // Save the updated queue
|
|
} else {
|
|
console.log('Item already exists in the queue. Skipping enqueue.');
|
|
}
|
|
}
|
|
|
|
// Dequeue: Remove an item from the front of the queue
|
|
dequeue(): T | null {
|
|
this.loadQueue(); // Ensure we load the latest queue
|
|
if (this.queue.length === 0) {
|
|
return null; // Queue is empty
|
|
}
|
|
const item = this.queue.shift() as T; // Remove the first item
|
|
this.saveQueue(); // Save the updated queue
|
|
return item;
|
|
}
|
|
|
|
// Peek: Get the item at the front of the queue without removing it
|
|
peek(): T | null {
|
|
this.loadQueue(); // Ensure we load the latest queue
|
|
return this.queue.length > 0 ? this.queue[0] : null;
|
|
}
|
|
|
|
// Check if the queue is empty
|
|
isEmpty(): boolean {
|
|
this.loadQueue(); // Ensure we load the latest queue
|
|
return this.queue.length === 0;
|
|
}
|
|
|
|
// Get the length of the queue
|
|
length(): number {
|
|
this.loadQueue(); // Ensure we load the latest queue
|
|
return this.queue.length;
|
|
}
|
|
|
|
// Clear the entire queue
|
|
clearQueue(): void {
|
|
this.acquireLock(); // Acquire the lock
|
|
|
|
try {
|
|
this.queue = []; // Clear the queue
|
|
fs.writeFileSync(this.filePath, JSON.stringify(this.queue, null, 2), 'utf8');
|
|
} finally {
|
|
this.releaseLock(); // Release the lock
|
|
}
|
|
}
|
|
}
|