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(); } async function buildDestinationIndexes(destinationRoot, onProgress) { const folderIndex = new Map(); const cadKeyIndex = new Set(); if (!destinationRoot || !(await fs.pathExists(destinationRoot))) { return { folderIndex, cadKeyIndex }; } 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; for (const entry of topEntries) { if (!entry.isFile()) continue; const cadInfo = getCadInfo(entry.name); if (cadInfo) { scannedFiles += 1; cadKeyIndex.add(toCadKey(cadInfo)); } } 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); } 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 };