diff --git a/.old/index.js b/.old/index.js new file mode 100644 index 0000000..ea178b3 --- /dev/null +++ b/.old/index.js @@ -0,0 +1,98 @@ +'use strict'; +var os = require('os'); +var path = require('path'); +var process = require('child_process'); +var events = require('events'); +var util = require('util'); +var spawn = require('win-spawn'); +var Promise = require('promise'); + +// Create an Api class that extends the EventEmitter. +function Api(name) { + this.name = (name) ? name : '7z'; +} +util.inherits(Api, events.EventEmitter); + +function concatSwitches(switches) { + var r = ''; + var c = ''; + for (var s in switches) { + c = switches[s]; + c = (c === true) ? '' : c; + r += '-' + s + c; + } +} + +// Adds files to archive. +Api.prototype.add = Promise.denodeify(function (archive, files, switches, cb) { + + // When no switches are speciied use the given function as the callback and + // set switches as an empty object. + if (typeof switches === 'function') { + cb = switches; + switches = {}; + } + archive = path.resolve(archive); + + +}); + + +// Test integrity of archive. +Api.prototype.test = Promise.denodeify(function (archive, callback) { + + archive = path.resolve(archive); + var cmd = '7z t ' + archive; + process.exec(cmd, function (err, stdout) { + if (err) return callback(err, null); + // Parse each line of the stdout and build an array of files (and + // directories) tested. The / is used in the result instead of the default + // OS path separator. + var r = []; + stdout.split(os.EOL).forEach(function (_line) { + if (_line.substr(0, 12) === 'Testing ') { + r.push(_line.substr(12, _line.length).replace(path.sep, '/')); + } + }); + return callback(null, r); + }); + +}); + + +// Extract with full paths. +Api.prototype.extract = Promise.denodeify(function (archive, dest, callback) { + + archive = path.resolve(archive); + dest = path.resolve(dest); + var self = this; + var err = null; + var cmd = ('x ' + archive + ' -y -o' + dest).split(' '); + var run = spawn('7z', cmd, { stdio: 'pipe' }); + + run.on('close', function (code) { + if (err) return callback(err); + return callback(null); + }); + + run.stdout.on('data', function (data) { + // When an stdout is emitted, parse it. If an error is detected in the body + // of the stdout create an new error with the 7-Zip error message as the + // error's message. Otherwise emit a `data` event with the processed files + // and directories as an array. + var files = []; + var regex = new RegExp('Error:' + os.EOL + '(.*)', 'g'); + var res = regex.exec(data.toString()); + if (res) err = new Error(res[1]); + data.toString().split(os.EOL).forEach(function (_line) { + if (_line.substr(0, 12) === 'Extracting ') { + files.push(_line.substr(12, _line.length).replace(path.sep, '/')); + } + }); + self.emit('data', files); + }); + +}); + + +module.exports = Api; diff --git a/.old/test.js b/.old/test.js new file mode 100644 index 0000000..91f7154 --- /dev/null +++ b/.old/test.js @@ -0,0 +1,112 @@ +'use strict'; +var Api = require('../index'); +var expect = require('chai').expect; +var path = require('path'); +var fs = require('fs'); +var rimraf = require('rimraf'); + +describe('The Api object', function () { + + after(function () { + rimraf.sync('.tmp/test'); + }); + + it('should respond to events methods', function () { + var api = new Api(); + expect(api).to.respondTo('emit'); + expect(api).to.respondTo('on'); + }); + + it('should have `7z` as default name', function () { + var api = new Api(); + expect(api.name).to.eql('7z'); + }); + + it('should be nameable', function () { + var api = new Api('testName'); + expect(api.name).to.eql('testName'); + }); + +}); + +describe('`add` function', function() { + + it('should get an error when 7z gets an error', function (done) { + var api = new Api(); + api.add('myArchive.7z', 'files', { i:'az', r: true }, function (s) { + console.log(s); + }); + api.add('myArchive.7z', 'files', function (s) { + console.log(s); + }); + done(); + }); + +}); + +describe('`test` function', function(){ + + it('should get an error when 7z gets an error', function (done) { + var api = new Api(); + api.test('test/nothere.7z', function (err) { + expect(err.message.substr(0, 14)).to.eql('Command failed'); + done(); + }); + }); + + it('should get a list of files and directories', function (done) { + var api = new Api(); + api.test('test/zip.7z', function (err, files) { + expect(files.length).to.be.at.least(6); + done(); + }); + }); + + it('should be compatible with Promise', function (done) { + var api = new Api(); + api.test('test/zip.7z').then(function (files) { + expect(files.length).to.be.at.least(6); + done(); + }); + }); + +}); + +describe('`extract` function', function() { + + it('should get an error when 7z gets an error', function (done) { + var api = new Api(); + api.extract('test/nothere.7z', '.tmp/test', function (err) { + expect(err.message).to.contain('archive'); + done(); + }); + }); + + it('should get a list of files and directories', function (done) { + var api = new Api(); + api.on('data', function (files) { + expect(files).to.be.an('array'); + done(); + }); + api.extract('test/zip.7z', '.tmp/test', function () {}); + }); + + it('should get no error on success', function (done) { + var api = new Api(); + api.extract('test/zip.7z', '.tmp/test', function (err) { + var file = path.resolve('.tmp/test/zip/file1.txt'); + expect(fs.existsSync(file)).to.eql(true); + expect(err).to.eql(null); + done(); + }); + }); + + it('should be compatible with Promise', function (done) { + var api = new Api(); + api.extract('test/nothere.7z', '.tmp/test').then(null, function (err) { + expect(err.message).to.contain('archive'); + done(); + }); + }); + +}); diff --git a/lib/extractFull.js b/lib/extractFull.js new file mode 100644 index 0000000..0d9e488 --- /dev/null +++ b/lib/extractFull.js @@ -0,0 +1,38 @@ +'use strict'; +var path = require('path'); +var Q = require('q'); +var u = { + run: require('../util/run') +}; + +module.exports = function (archive, dest, options, cb) { + return Q.Promise(function (resolve, reject, notify) { + + //TODO: parse options, -y always true? + var opts = ''; + var command = '7z x ' + archive + ' -o' + dest + ' ' + opts; + u.run(command) + + // When a stdout is emitted, parse each line and search for a pattern. When + // the pattern is found, extract the file (or directory) name from it and + // pass it to an array. Finally returns this array. + .progress(function (data) { + var entries = []; + data.split('\n').forEach(function (line) { + if (line.substr(0, 12) === 'Extracting ') { + entries.push(line.substr(12, line.length).replace(path.sep, '/')); + } + }); + notify(entries); + }) + + .then(function () { + resolve(); + }) + + .catch(function (err) { + reject(err); + }); + + }).nodeify(cb); +}; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..489ba7e --- /dev/null +++ b/lib/index.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + extractFull: require('./extractFull') +}; \ No newline at end of file diff --git a/package.json b/package.json index 8d8fbf1..c072dad 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "7z", "version": "0.0.1", "description": "A Node.js wrapper for 7-Zip", - "main": "index.js", + "main": "lib/index.js", "scripts": { "test": "mocha", "coverage": "istanbul cover node_modules/mocha/bin/_mocha --dir .tmp/coverage", @@ -25,7 +25,7 @@ }, "homepage": "http://quentinrossetti.github.io/node-7zip", "dependencies": { - "promise": "^5.0.0", + "q": "^2.0.2", "win-spawn": "^2.0.0" }, "devDependencies": { diff --git a/test/lib/extractFull.js b/test/lib/extractFull.js new file mode 100644 index 0000000..e16c7c0 --- /dev/null +++ b/test/lib/extractFull.js @@ -0,0 +1,35 @@ +/*global describe, it, afterEach */ +var expect = require('chai').expect; +var fs = require('fs'); +var rimraf = require('rimraf'); +var extractFull = require('../../lib/extractFull'); + +describe('Module: `extractFull`', function () { + + afterEach(function () { rimraf.sync('.tmp/test'); }); + + it('should return an error on 7z error', function (done) { + extractFull('test/nothere.7z', '.tmp/test') + .catch(function (err) { + expect(err).to.be.an.instanceof(Error); + done(); + }); + }); + + it('should return entries on progress', function (done) { + extractFull('test/zip.7z', '.tmp/test') + .progress(function (entries) { + expect(entries.length).to.be.at.least(1); + done(); + }); + }); + + it('should extract on the right path', function (done) { + extractFull('test/zip.7z', '.tmp/test') + .then(function () { + expect(fs.existsSync('.tmp/test/zip')).to.be.eql(true); + done(); + }); + }); + +}); \ No newline at end of file diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..63b406d --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ +--recursive \ No newline at end of file diff --git a/test/util/run.js b/test/util/run.js new file mode 100644 index 0000000..d597f40 --- /dev/null +++ b/test/util/run.js @@ -0,0 +1,38 @@ +/*global describe, it */ +var expect = require('chai').expect; +var run = require('../../util/run'); + +describe('Util: `run`', function () { + + it('should return an error with invalid command type', function (done) { + run(0).catch(function (err) { + expect(err.message).to.eql('Command must be a string'); + done(); + }); + }); + + it('should return an error on when 7z gets one', function (done) { + run('7z ?').catch(function (err) { + expect(err.message).to.eql('Incorrect command line'); + done(); + }); + }); + + it('should return an stdout on progress', function (done) { + run('7z') + .progress(function (data) { + expect(data).to.be.a('string'); + }) + .then(function () { + done(); + }); + }); + + it('should work as Node.js-style callback', function (done) { + run(0, function (err) { + expect(err.message).to.be.a('string'); + done(); + }); + }); + +}); \ No newline at end of file diff --git a/test/util/switches.js b/test/util/switches.js new file mode 100644 index 0000000..c45c142 --- /dev/null +++ b/test/util/switches.js @@ -0,0 +1,45 @@ +/*global describe, it */ +var expect = require('chai').expect; +var switches = require('../../util/switches'); + +describe('Util: `switches`', function () { + + it('should return deflaut flags with no args', function () { + expect(switches({})).to.eql('-ssc -y'); + }); + + it('should return -ssc with flag { ssc: true }', function () { + expect(switches({ ssc: true })).to.eql('-ssc -y'); + }); + + it('should return -ssc- with flag { ssc: false }', function () { + expect(switches({ ssc: false })).to.eql('-ssc- -y'); + }); + + it('should return non default booleans when specified', function () { + var r = switches({ + so : true, + spl: true, + ssw: true, + y : false + }); + expect(r).to.contain('-so'); + expect(r).to.contain('-spl'); + expect(r).to.contain('-ssc'); + expect(r).to.contain('-ssw'); + expect(r).not.to.contain('-y'); + }); + + it('should return complex values when needed', function () { + var r = switches({ + ssc : true, + ssw : true, + m : '0=BCJ -m1=LZMA:d=21' + }); + expect(r).to.contain('-ssc'); + expect(r).to.contain('-ssw'); + expect(r).to.contain('m0=BCJ -m1=LZMA:d=21'); + expect(r).to.contain('-y'); + }); + +}); \ No newline at end of file diff --git a/util/run.js b/util/run.js new file mode 100644 index 0000000..cfc333e --- /dev/null +++ b/util/run.js @@ -0,0 +1,43 @@ +'use strict'; +var os = require('os'); +var spawn = require('win-spawn'); +var Q = require('q'); + +/** + * @function run + * @param {string} command The command to run + */ +module.exports = function (command, cb) { + return Q.Promise(function (resolve, reject, notify) { + + // Parse the command variable. If the command is not a string reject the + // Promise. Otherwise transform the command into two variables: the command + // name and the arguments. + if (typeof command !== 'string') { + return reject(new Error('Command must be a string')); + } + var args = command.split(' '); + var cmd = args[0]; + args.shift(); + + // When an stdout is emitted, parse it. If an error is detected in the body + // of the stdout create an new error with the 7-Zip error message as the + // error's message. Otherwise progress with the processed files and + // directories as an array. + var err; + var reg = new RegExp('Error:' + os.EOL + '(.*)', 'g'); + var run = spawn(cmd, args, { stdio: 'pipe' }); + run.stdout.on('data', function (data) { + var res = reg.exec(data.toString()); + if (res) { + err = new Error(res[1]); + } + notify(data.toString()); + }); + run.on('close', function (code) { + if (code === 0) return resolve(); + reject(err); + }); + + }).nodeify(cb); +}; diff --git a/util/switches.js b/util/switches.js new file mode 100644 index 0000000..4d4eb1b --- /dev/null +++ b/util/switches.js @@ -0,0 +1,33 @@ +'use strict'; + +module.exports = function (switches) { + + var r = ''; + // Set defalut values of boolean switches + switches.so = (switches.so === true) ? true : false; + switches.spl = (switches.spl === true) ? true : false; + switches.ssc = (switches.ssc === false) ? false : true ; + switches.ssw = (switches.ssw === true) ? true : false; + switches.y = (switches.y === false) ? false : true ; + + for (var s in switches) { + var concat = ''; + + if (switches[s] === true) { + concat += '-' + s + ' '; + } + if (typeof switches[s] !== 'boolean') { + concat += '-' + s + switches[s] + ' '; + } + + // Special treatment for `-ssc` + if (s === 'ssc') { + concat = (switches.ssc === true) ? '-ssc ' : '-ssc- '; + } + + r += concat; + } + + return r.trim(); + +}; \ No newline at end of file