Repository navigation
Expand file tree
/
Copy pathtask.js
More file actions
96 lines (84 loc) · 3.21 KB
/
Copy pathtask.js
File metadata and controls
96 lines (84 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
const fs = require("node:fs");
const path = require("node:path");
const tasksFilePath = process.env.TASKS_FILE || path.join(__dirname, "tasks.json");
const statuses = new Set(["todo", "in-progress", "done"]);
function readTasks() {
let data;
try { data = fs.readFileSync(tasksFilePath, "utf8"); }
catch (error) { if (error.code === "ENOENT") return []; throw error; }
const tasks = data.trim() ? JSON.parse(data) : [];
if (!Array.isArray(tasks) || tasks.some(t => !t || !Number.isSafeInteger(t.id) ||
t.id < 1 || typeof t.description !== "string" || !statuses.has(t.status)) ||
new Set(tasks.map(t => t.id)).size !== tasks.length) {
throw new Error("Invalid tasks file; recover it before making changes.");
}
return tasks;
}
function writeTasks(tasks) {
const temporary = tasksFilePath + "." + process.pid + ".tmp";
try {
fs.writeFileSync(temporary, JSON.stringify(tasks, null, 2));
fs.renameSync(temporary, tasksFilePath);
} finally {
if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
}
}
function requireDescription(description) {
if (typeof description !== "string" || !description.trim()) {
throw new Error("Task description cannot be empty.");
}
return description.trim();
}
function requireId(id) {
if (!Number.isSafeInteger(id) || id < 1) throw new Error("ID must be a positive integer.");
}
function findTask(tasks, id) {
requireId(id);
const task = tasks.find(t => t.id === id);
if (!task) throw new Error(`Task with ID ${id} not found.`);
return task;
}
function addTask(description) {
description = requireDescription(description);
const tasks = readTasks();
const id = tasks.reduce((maximum, task) => Math.max(maximum, task.id), 0) + 1;
requireId(id);
const now = new Date().toISOString();
tasks.push({ id, description, status: "todo", createdAt: now, updatedAt: now });
writeTasks(tasks);
console.log(`Task added successfully (ID: ${id})`);
}
function updateTask(id, description) {
description = requireDescription(description);
const tasks = readTasks();
const task = findTask(tasks, id);
task.description = description;
task.updatedAt = new Date().toISOString();
writeTasks(tasks);
console.log(`Task ${id} updated successfully.`);
}
function deleteTask(id) {
const tasks = readTasks();
findTask(tasks, id);
writeTasks(tasks.filter(t => t.id !== id));
console.log(`Task ${id} deleted successfully.`);
}
function mark(id, status) {
const tasks = readTasks();
const task = findTask(tasks, id);
task.status = status;
task.updatedAt = new Date().toISOString();
writeTasks(tasks);
console.log(`Task ${id} marked as ${status}.`);
}
function listTasks(statusFilter = null) {
if (statusFilter && !statuses.has(statusFilter)) throw new Error("Invalid status filter.");
const tasks = readTasks().filter(t => !statusFilter || t.status === statusFilter);
if (!tasks.length) console.log("No tasks found.");
for (const task of tasks) console.log(`ID: ${task.id}, Description: ${task.description}, Status: ${task.status}, Created At: ${task.createdAt}, Updated At: ${task.updatedAt}`);
}
module.exports = {
addTask, updateTask, deleteTask, listTasks,
markInProgress: id => mark(id, "in-progress"),
markDone: id => mark(id, "done"),
};