const fs = require('fs-extra'); const path = require('path'); const { getCadInfo } = require('./router'); const CODE_FOLDER_REGEX = /^\d{3}$/; const MAX_SCAN_DEPTH = 6; function toCadKey(cadInfo) { return String(cadInfo?.key || '').toLowerCase(); } /** * Scansiona la cartella di destinazione in un unico passaggio parallelo e * costruisce contemporaneamente: * - folderIndex: Map (per il routing) * - cadKeyIndex: Set (per il rilevamento duplicati) * * Legge prima le sottocartelle di primo livello (1 sola readdir) per ottenere * il totale e mostrare una barra determinata durante la scansione. */ async function buildDestinationIndexes(destinationRoot, onProgress) { const folderIndex = new Map(); const cadKeyIndex = new Set(); if (!destinationRoot || !(await fs.pathExists(destinationRoot))) { return { folderIndex, cadKeyIndex }; } // Lettura di primo livello: 1 sola chiamata per avere il totale let topEntries; try { topEntries = await fs.readdir(destinationRoot, { withFileTypes: true }); } catch { return { folderIndex, cadKeyIndex }; } const topDirs = topEntries .filter((e) => e.isDirectory()) .map((e) => ({ name: e.name, fullPath: path.join(destinationRoot, e.name) })); const total = topDirs.length; let completed = 0; let scannedFiles = 0; // Registra i file CAD al primo livello (non cartelle) for (const entry of topEntries) { if (!entry.isFile()) continue; const cadInfo = getCadInfo(entry.name); if (cadInfo) { scannedFiles += 1; cadKeyIndex.add(toCadKey(cadInfo)); } } // Walk ricorsivo del sottoalbero di una cartella di primo livello async function walkSubtree(dirPath, depth) { if (depth > MAX_SCAN_DEPTH) { return; } let entries; try { entries = await fs.readdir(dirPath, { withFileTypes: true }); } catch { return; } const subdirPromises = []; for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { if (CODE_FOLDER_REGEX.test(entry.name)) { const rows = folderIndex.get(entry.name) || []; rows.push(fullPath); folderIndex.set(entry.name, rows); } subdirPromises.push(walkSubtree(fullPath, depth + 1)); } else if (entry.isFile()) { const cadInfo = getCadInfo(entry.name); if (cadInfo) { scannedFiles += 1; onProgress?.({ phase: 'index-dup', scanned: scannedFiles, file: entry.name }); cadKeyIndex.add(toCadKey(cadInfo)); } } } await Promise.all(subdirPromises); } // Processa tutte le cartelle di primo livello in parallelo await Promise.all( topDirs.map(async ({ name, fullPath }) => { onProgress?.({ phase: 'index-dest', current: completed, total, folder: name }); if (CODE_FOLDER_REGEX.test(name)) { const rows = folderIndex.get(name) || []; rows.push(fullPath); folderIndex.set(name, rows); } await walkSubtree(fullPath, 1); completed += 1; onProgress?.({ phase: 'index-dest', current: completed, total, folder: name }); }) ); return { folderIndex, cadKeyIndex }; } module.exports = { buildDestinationIndexes, toCadKey };