Skip to content

Commit 3330607

Browse files
committed
feat: implement tree-based file sync logging with hierarchical display
1 parent da20c6f commit 3330607

1 file changed

Lines changed: 116 additions & 12 deletions

File tree

src/utils/gh-sync-utils/sync-utils.ts

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,26 +273,130 @@ export function generateSyncSummary(results: FileSyncResult[]): SyncSummary {
273273
return summary;
274274
}
275275

276+
interface TreeNode {
277+
name: string;
278+
children: Map<string, TreeNode>;
279+
files: string[];
280+
action: FileAction;
281+
}
282+
283+
/**
284+
* Builds a tree structure from file paths
285+
*/
286+
function buildTree(
287+
files: { path: string; action: FileAction }[]
288+
): Map<FileAction, TreeNode> {
289+
const actionTrees = new Map<FileAction, TreeNode>();
290+
291+
for (const { path, action } of files) {
292+
if (!actionTrees.has(action)) {
293+
actionTrees.set(action, {
294+
name: "",
295+
children: new Map(),
296+
files: [],
297+
action,
298+
});
299+
}
300+
301+
const parts = path.split("/");
302+
const filename = parts.pop() || "";
303+
let currentNode = actionTrees.get(action)!;
304+
305+
// Traverse or create the directory structure
306+
for (const part of parts) {
307+
if (!currentNode.children.has(part)) {
308+
currentNode.children.set(part, {
309+
name: part,
310+
children: new Map(),
311+
files: [],
312+
action,
313+
});
314+
}
315+
currentNode = currentNode.children.get(part)!;
316+
}
317+
318+
// Add the file to the current directory
319+
if (filename) {
320+
currentNode.files.push(filename);
321+
}
322+
}
323+
324+
return actionTrees;
325+
}
326+
276327
/**
277-
* Logs detailed information about file sync operations
328+
* Formats a tree node and its children as a string
329+
*/
330+
function formatTreeNode(
331+
node: TreeNode,
332+
isLast: boolean,
333+
prefix: string[],
334+
output: string[]
335+
): void {
336+
// Skip root node if it's empty
337+
if (node.name) {
338+
const connector = isLast ? "└── " : "├── ";
339+
output.push([...prefix, connector + node.name].join(""));
340+
}
341+
342+
const newPrefix = [...prefix, isLast ? " " : "│ "];
343+
344+
// Process directories first
345+
const sortedDirs = Array.from(node.children.entries()).sort(
346+
([nameA], [nameB]) => nameA.localeCompare(nameB)
347+
);
348+
349+
for (let i = 0; i < sortedDirs.length; i++) {
350+
const dirEntry = sortedDirs[i];
351+
if (!dirEntry) continue;
352+
const [_, child] = dirEntry;
353+
const isLastChild = i === sortedDirs.length - 1 && node.files.length === 0;
354+
formatTreeNode(child, isLastChild, newPrefix, output);
355+
}
356+
357+
// Then process files
358+
const sortedFiles = [...node.files].sort();
359+
for (let i = 0; i < sortedFiles.length; i++) {
360+
const isLastFile = i === sortedFiles.length - 1;
361+
const connector = isLastFile ? "└── " : "├── ";
362+
output.push([...newPrefix, connector + sortedFiles[i]].join(""));
363+
}
364+
}
365+
366+
/**
367+
* Logs detailed information about file sync operations, grouped by action type
278368
*/
279369
export function logSyncDetails(results: FileSyncResult[]): void {
280370
if (!results.length) return;
281371

282-
logger.log(""); // Empty line before details
283-
logger.info("File synchronization details:");
284-
logger.log("=".repeat(80));
372+
const output: string[] = ["File synchronization details:", "=".repeat(80)];
285373

286-
for (const result of results) {
287-
if (result.actionResult.action !== FileAction.NONE) {
288-
logger.log(
289-
`${result.actionResult.action.toString().padEnd(10)} ${result.operation.relativeLocalPath}`
290-
);
291-
}
374+
// Prepare files for tree building
375+
const files = results
376+
.filter(result => result.actionResult.action !== FileAction.NONE)
377+
.map(result => ({
378+
path: result.operation.relativeLocalPath,
379+
action: result.actionResult.action,
380+
}));
381+
382+
// Build tree structure
383+
const actionTrees = buildTree(files);
384+
385+
// Sort actions for consistent output
386+
const sortedActions = Array.from(actionTrees.entries()).sort(
387+
([actionA], [actionB]) => actionA.localeCompare(actionB)
388+
);
389+
390+
// Generate output
391+
for (const actionTree of sortedActions) {
392+
if (!actionTree) continue;
393+
const [action, tree] = actionTree;
394+
output.push(`\n${action.toUpperCase()}:`);
395+
formatTreeNode(tree, true, [], output);
292396
}
293397

294-
logger.log("=".repeat(80));
295-
logger.log(""); // Empty line after details
398+
output.push("\n" + "=".repeat(80));
399+
logger.log(output.join("\n"));
296400
}
297401

298402
/**

0 commit comments

Comments
 (0)