diff --git a/src/shell/shell.js b/src/shell/shell.js index 3fd1a58..48d7ec1 100644 --- a/src/shell/shell.js +++ b/src/shell/shell.js @@ -266,6 +266,68 @@ Shell.prototype.ls = function(dir, options, callback) { list(dir, callback); }; +Shell.prototype.du = function(dir, callback) { + var sh = this; + var fs = sh.fs; + + callback = callback || function(){}; + + if(!dir) { + callback(new Errors.EINVAL('Missing dir argument')); + return; + } + + function list(path, callback) { + var pathname = Path.resolve(sh.pwd(), path); + var result = []; + + fs.readdir(pathname, function(error, entries) { + if(error) { + callback(error); + return; + } + + function getDirEntry(name, callback) { + name = Path.join(pathname, name); + fs.stat(name, function(error, stats) { + if(error) { + callback(error); + return; + } + var entry = { + path: Path.basename(name), + links: stats.nlinks, + size: stats.size, + modified: stats.mtime, + type: stats.type + }; + + if(stats.type === 'DIRECTORY') { + list(Path.join(pathname, entry.path), function(error, items) { + if(error) { + callback(error); + return; + } + entry.contents = items; + result.push(entry); + callback(); + }); + } else { + result.push(entry); + callback(); + } + }); + } + + async.eachSeries(entries, getDirEntry, function(error) { + callback(error, result); + }); + }); + } + + list(dir, callback); +}; + /** * Removes the file or directory at `path`. If `path` is a file * it will be removed. If `path` is a directory, it will be diff --git a/tests/spec/shell/du.spec.js b/tests/spec/shell/du.spec.js new file mode 100644 index 0000000..cc8ddd9 --- /dev/null +++ b/tests/spec/shell/du.spec.js @@ -0,0 +1,167 @@ +var Filer = require('../../..'); +var util = require('../../lib/test-utils.js'); +var expect = require('chai').expect; + +describe('FileSystemShell.du', function() { + beforeEach(util.setup); + afterEach(util.cleanup); + + it('should be a function', function() { + var shell = util.shell(); + expect(shell.du).to.be.a('function'); + }); + + it('should fail when dirs argument is absent', function(done) { + var fs = util.fs(); + var shell = fs.Shell(); + + shell.cat(null, function(error, list) { + expect(error).to.exist; + expect(error.code).to.equal("EINVAL"); + expect(list).not.to.exist; + done(); + }); + }); + + it('should return size of the files', function(done) { + var fs = util.fs(); + var shell = fs.Shell(); + var contents = "a"; + var contents2 = "bb"; + + fs.writeFile('/file', contents, function(err) { + if(err) throw err; + + fs.writeFile('/file2', contents2, function(err) { + if(err) throw err; + + shell.du('/', function(err, list) { + expect(err).not.to.exist; + expect(list.length).to.equal(2); + + var item0 = list[0]; + expect(item0.path).to.equal('file'); + expect(item0.size).to.equal(1); + expect(item0.contents).not.to.exist; + + var item1 = list[1]; + expect(item1.path).to.equal('file2'); + expect(item1.size).to.equal(2); + expect(item0.contents).not.to.exist; + + done(); + }); + }); + }); + }); + + it('should return the shallow contents of a dir tree', function(done) { + var fs = util.fs(); + var shell = fs.Shell(); + var contents = "a"; + + fs.mkdir('/dir', function(err) { + if(err) throw err; + + fs.mkdir('/dir/dir2', function(err) { + if(err) throw err; + + fs.writeFile('/dir/file', contents, function(err) { + if(err) throw err; + + fs.writeFile('/dir/file2', contents, function(err) { + if(err) throw err; + + shell.du('/dir', function(err, list) { + expect(err).not.to.exist; + expect(list.length).to.equal(3); + + // We shouldn't rely on the order we'll get the listing + list.forEach(function(item, i, arr) { + switch(item.path) { + case 'dir2': + expect(item.size).to.be.a('number'); + expect(item.contents).not.to.exist; + break; + case 'file': + case 'file2': + expect(item.size).to.equal(1); + expect(item.contents).not.to.exist; + break; + default: + // shouldn't happen + expect(true).to.be.false; + break; + } + + if(i === arr.length -1) { + done(); + } + }); + }); + }); + }); + }); + }); + }); + + it('should return the deep contents of a dir tree', function(done) { + var fs = util.fs(); + var shell = fs.Shell(); + var contents = "a"; + + fs.mkdir('/dir', function(err) { + if(err) throw err; + + fs.mkdir('/dir/dir2', function(err) { + if(err) throw err; + + fs.writeFile('/dir/dir2/file', contents, function(err) { + if(err) throw err; + + fs.writeFile('/dir/file', contents, function(err) { + if(err) throw err; + + fs.writeFile('/dir/file2', contents, function(err) { + if(err) throw err; + + shell.du('/dir', { recursive: true }, function(err, list) { + expect(err).not.to.exist; + expect(list.length).to.equal(3); + + // We shouldn't rely on the order we'll get the listing + list.forEach(function(item, i, arr) { + switch(item.path) { + case 'dir2': + + expect(item.size).to.be.a('number'); + expect(item.contents).to.exist; + expect(item.contents.length).to.equal(1); + var contents0 = item.contents[0]; + expect(contents0.path).to.equal('file'); + expect(contents0.size).to.equal(1); + expect(contents0.contents).not.to.exist; + break; + case 'file': + case 'file2': + expect(item.size).to.equal(1); + expect(item.contents).not.to.exist; + break; + default: + // shouldn't happen + expect(true).to.be.false; + break; + } + + if(i === arr.length -1) { + done(); + } + }); + }); + }); + }); + }); + }); + }); + }); +});