synapse_fs
Version:
A simple file search utility.
169 lines (145 loc) • 5.84 kB
JavaScript
//Each node has it's own thread count, starting at 0
var fs = require('fs');
//Require, but without setting a reference, and instead just directly instantiating a new instance, with constructor parameter of 0 (Singleton pattern)
var threadCounter = new (require('./ThreadCounter/ThreadCounter'))(0);
var File = require('./file');
var globalFileIndex = {};
var rootSearchPath;
/*
* Functionality:
* - Reads some root directory provided by user input, for file names
* - Calls stat with the file names that were found, and retrieves the file type (File, Directory, Link, etc);
* - Returns an objectResult to the callback function, listing all Files/Directories & their properties.
*/
function FileSearchEngine (options, callback, threadId)
{
//if dirName == '/' then we are looking at a drive name (root node): / or c:\
if (options && options.searchPath)
{
/*if (options.searchPath == '/')
this.searchPath = options.searchPath;
else*/
this.searchPath = options.searchPath + '/';
}
else
throw 'Error! A search path has not been provided.';
if (options && Number(options.maxDepth) >= 0)
{
this.maxDepth = options.maxDepth;
if (!this.currenDepth)
this.currentDepth = 0;
}
if (options && Number(options.currentDepth) >= 0)
this.currentDepth = options.currentDepth;
this.remainingFileCount = 0;
this.callback = callback;
if (threadId)
this.threadId = threadId;
else
this.threadId = threadCounter.newThread(options.searchPath);
};
FileSearchEngine.prototype.constructor = FileSearchEngine;
FileSearchEngine.prototype.list = function list ()
{
fs.readdir(this.searchPath, this.readDirHandler.bind(this));
};
/*
* Reads the contents of a directory. The callback gets two arguments (err, files) where files is an array of the names
* of the files in the directory excluding '.' and '..'.
*/
FileSearchEngine.prototype.readDirHandler = function readDirHandler (error, dirFileList)
{
if (error)
{
threadCounter.closeThread(this.threadId);
console.log(error);
this.callback(error);
this.rootSearchPath = null;
globalFileIndex = {};
}
else if (threadCounter.isActiveThread(this.threadId))
{
//If count down counter is 0, Initialize it.
if (!this.remainingFileCount)
this.remainingFileCount = dirFileList.length;
//If a folder is empty at depth 0, close the thread, otherwise it'll just hang.
//Only counts for the first thread. We don't want to return if we find empty folders in other threads.
if (dirFileList.length == 0 && threadCounter.threadCount == 1)
{
threadCounter.closeThread(this.threadId);
this.callback(globalFileIndex);
rootSearchPath = null; //reset root search path
globalFileIndex = {}; //Need to reset the file index, since it's global it will retain it's state.
}
else if (dirFileList.length == 0) //Still need to close the thread when an empty folder is detected at a depth > 0
threadCounter.closeThread(this.threadId);
dirFileList.forEach(this.searchDir.bind(this));
}
};
FileSearchEngine.prototype.searchDir = function searchDir (fileName)
{
if (this.searchPath && this.searchPath.length > 0)
{
//At initialization, set the root search path
if (!rootSearchPath && threadCounter.threadCount == 1)
rootSearchPath = this.searchPath;
var absoluteFileName = this.searchPath + fileName;
fs.stat(absoluteFileName, this.handleStats.bind(this, absoluteFileName, fileName));
}
};
FileSearchEngine.prototype.handleStats = function handleStats (absoluteFileName, fileName, error, stats)
{
if (!(absoluteFileName in globalFileIndex))
globalFileIndex[absoluteFileName] = new File({absoluteFileName: absoluteFileName, fileName: fileName, rootSearchPath : rootSearchPath});
else
globalFileIndex[absoluteFileName].error = 'Duplicate file name detected';
if (error)
{
globalFileIndex[absoluteFileName].error = error;
globalFileIndex[absoluteFileName].isDirectory = false;
globalFileIndex[absoluteFileName].isFile = false;
this.ifDoneCallbackElseCloseThread(error);
}
else if (threadCounter.isActiveThread(this.threadId))
{
if (stats)
{
if (absoluteFileName in globalFileIndex && !absoluteFileName.error)
{
if (stats.isDirectory())
{
globalFileIndex[absoluteFileName].isDirectory = true;
globalFileIndex[absoluteFileName].isFile = false;
//This is a directory, so the absolute file name = directory path
if ((Number(this.currentDepth) >= 0 && Number(this.maxDepth) >= 0 && this.currentDepth < this.maxDepth) || typeof this.maxDepth == 'undefined' || Number(this.maxDepth) < 0)
new FileSearchEngine({searchPath: absoluteFileName, maxDepth: this.maxDepth, currentDepth: this.currentDepth + 1}, this.callback, threadCounter.newThread(absoluteFileName)).list();
}
if (stats.isFile())
{
globalFileIndex[absoluteFileName].isDirectory = false;
globalFileIndex[absoluteFileName].isFile = true;
}
}
else
console.log('path not found: ' + absoluteFileName);
}
this.ifDoneCallbackElseCloseThread();
}
};
FileSearchEngine.prototype.ifDoneCallbackElseCloseThread = function ifDoneCallbackElseCloseThread (resultOverride)
{
--this.remainingFileCount;
//Close this last thread
if (!this.remainingFileCount)
threadCounter.closeThread(this.threadId);
if (threadCounter.isDone() && !this.remainingFileCount && this.callback)
{
if (resultOverride)
this.callback(resultOverride);
else
this.callback(globalFileIndex);
rootSearchPath = null; //reset root search path
globalFileIndex = {}; //Need to reset the file index, since it's global it will retain it's state.
}
};
module.exports = FileSearchEngine;