Refactor code

This commit is contained in:
2025-01-15 16:46:07 +01:00
parent 464262d46d
commit 3b7307dac3
6 changed files with 106 additions and 64 deletions

View File

@@ -1,3 +1,2 @@
import {parse} from "./generated/grammar/task";
import { loadTasks } from "./loader";
console.log(parse("- [t]"));

104
src/loader/index.ts Normal file
View File

@@ -0,0 +1,104 @@
import fs from "fs";
import dayjs from "dayjs";
import { createInterface } from "readline";
import { parse } from "../generated/grammar/task";
import { Task as DefaultTask } from "../model";
import { ParseResult } from "../types/grammar/task";
import { Task, TaskPriority } from "../types/task";
/**
* Returns all tasks from specified directory and filters them with optional query.
*/
export async function loadTasks(directories: string[], query?: string): Promise<Task[]> {
const ctx = {
now: dayjs(),
LOWEST: TaskPriority.LOWEST,
LOW: TaskPriority.LOW,
NORMAL: TaskPriority.NORMAL,
MEDIUM: TaskPriority.MEDIUM,
HIGH: TaskPriority.HIGH,
HIGHEST: TaskPriority.HIGHEST
};
const filter = query && createFilter(query, ctx);
const tasks = await Promise.all(directories.map(readTasksFromDirectory));
return tasks.flat().filter(t => filter ? filter(t) : true);
}
/**
* Creates a filter function for tasks using a query string and context object.
* All context' properties will be passed as variables to the query string.
*/
function createFilter(query: string, context: Record<string, unknown>): (task: Task) => boolean {
const filter = new Function('$', ...Object.keys(context), `return ${query};`);
return (task: Task) => filter(task, ...Object.values(context));
}
/**
* Read all files in specific directory and returns all tasks from those files.
*/
async function readTasksFromDirectory(directory: string): Promise<Task[]> {
return walk(directory, readTasksFromFile);
}
/**
* Walks through a specific directory recursively and invokes visitor on each file.
* Returns a flat list of items returned by visitors.
*/
async function walk<T>(directory: string, visitor: (path: string) => Promise<T[]>): Promise<T[]> {
const list = [];
for(const file of fs.readdirSync(directory)) {
const path = `${directory}/${file}`;
if (fs.statSync(path).isDirectory()) {
const items = await walk(path, visitor);
list.push(...items);
} else {
const items = await visitor(path);
list.push(...items);
}
}
return list;
}
/**
* Reads all tasks from file path.
*/
async function readTasksFromFile(path: string): Promise<Task[]> {
const fileStream = fs.createReadStream(path);
const lines = createInterface({
input: fileStream,
crlfDelay: Infinity
});
const list: Task[] = [];
for await (const line of lines) {
const task = parseTask(line);
if(task) {
list.push(task);
}
}
return list;
}
/**
* Converts line to task model.
* If the line does not represent task, returns undefined.
*/
function parseTask(line: string): Task|undefined {
const item = parse(line) as ParseResult;
if (item.type === 'task') {
return new DefaultTask(item.data);
}
return undefined;
}

1
src/model/index.ts Normal file
View File

@@ -0,0 +1 @@
export {LazyTask as Task} from "./task";

View File

@@ -1,47 +0,0 @@
import fs from "fs";
import readline from "readline";
import { Task } from "../types/task";
import { parse } from "../parser";
export async function readTasksFromDirectory(directory: string): Promise<Task[]> {
return walk(directory, readTasksFromFile);
}
export async function walk<T>(directory: string, visitor: (path: string) => Promise<T[]>): Promise<T[]> {
const list = [];
for(const file of fs.readdirSync(directory)) {
const path = `${directory}/${file}`;
if (fs.statSync(path).isDirectory()) {
const items = await walk(path, visitor);
list.push(...items);
} else {
const items = await visitor(path);
list.push(...items);
}
}
return list;
}
export async function readTasksFromFile(path: string): Promise<Task[]> {
const fileStream = fs.createReadStream(path);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
const list: Task[] = [];
for await (const line of rl) {
const task = parse(line);
if(task) {
list.push(task);
}
}
return list;
}

View File

@@ -1,15 +0,0 @@
import {parse as rawParse} from "../generated/grammar/task";
import { ParseResult } from "../types/grammar/task";
import { Task } from "../types/task";
import { LazyTask } from "./task";
export function parse(line: string): Task|undefined {
const item = rawParse(line) as ParseResult;
if (item.type === 'task') {
return new LazyTask(item.data);
}
return undefined;
}