blob: 8c157a90ef2f327e4671332f50f9060bc0ab1e13 [file] [log] [blame]
(function() {
var Entry, EventEmitter, Fs, Path, Promise, Protocol, PullTransfer, PushTransfer, Stats, Sync, debug,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Fs = require('fs');
Path = require('path');
Promise = require('bluebird');
EventEmitter = require('events').EventEmitter;
debug = require('debug')('adb:sync');
Protocol = require('./protocol');
Stats = require('./sync/stats');
Entry = require('./sync/entry');
PushTransfer = require('./sync/pushtransfer');
PullTransfer = require('./sync/pulltransfer');
Sync = (function(_super) {
var DATA_MAX_LENGTH, DEFAULT_CHMOD, TEMP_PATH;
__extends(Sync, _super);
TEMP_PATH = '/data/local/tmp';
DEFAULT_CHMOD = 0x1a4;
DATA_MAX_LENGTH = 65536;
Sync.temp = function(path) {
return "" + TEMP_PATH + "/" + (Path.basename(path));
};
function Sync(connection) {
this.connection = connection;
this.parser = this.connection.parser;
}
Sync.prototype.stat = function(path, callback) {
this._sendCommandWithArg(Protocol.STAT, path);
return this.parser.readAscii(4).then((function(_this) {
return function(reply) {
switch (reply) {
case Protocol.STAT:
return _this.parser.readBytes(12).then(function(stat) {
var mode, mtime, size;
mode = stat.readUInt32LE(0);
size = stat.readUInt32LE(4);
mtime = stat.readUInt32LE(8);
if (mode === 0) {
return _this._enoent(path);
} else {
return new Stats(mode, size, mtime);
}
});
case Protocol.FAIL:
return _this._readError();
default:
return _this.parser.unexpected(reply, 'STAT or FAIL');
}
};
})(this)).nodeify(callback);
};
Sync.prototype.readdir = function(path, callback) {
var files, readNext;
files = [];
readNext = (function(_this) {
return function() {
return _this.parser.readAscii(4).then(function(reply) {
switch (reply) {
case Protocol.DENT:
return _this.parser.readBytes(16).then(function(stat) {
var mode, mtime, namelen, size;
mode = stat.readUInt32LE(0);
size = stat.readUInt32LE(4);
mtime = stat.readUInt32LE(8);
namelen = stat.readUInt32LE(12);
return _this.parser.readBytes(namelen).then(function(name) {
name = name.toString();
if (!(name === '.' || name === '..')) {
files.push(new Entry(name, mode, size, mtime));
}
return readNext();
});
});
case Protocol.DONE:
return _this.parser.readBytes(16).then(function(zero) {
return files;
});
case Protocol.FAIL:
return _this._readError();
default:
return _this.parser.unexpected(reply, 'DENT, DONE or FAIL');
}
});
};
})(this);
this._sendCommandWithArg(Protocol.LIST, path);
return readNext().nodeify(callback);
};
Sync.prototype.push = function(contents, path, mode) {
if (typeof contents === 'string') {
return this.pushFile(contents, path, mode);
} else {
return this.pushStream(contents, path, mode);
}
};
Sync.prototype.pushFile = function(file, path, mode) {
if (mode == null) {
mode = DEFAULT_CHMOD;
}
mode || (mode = DEFAULT_CHMOD);
return this.pushStream(Fs.createReadStream(file), path, mode);
};
Sync.prototype.pushStream = function(stream, path, mode) {
if (mode == null) {
mode = DEFAULT_CHMOD;
}
mode |= Stats.S_IFREG;
this._sendCommandWithArg(Protocol.SEND, "" + path + "," + mode);
return this._writeData(stream, Math.floor(Date.now() / 1000));
};
Sync.prototype.pull = function(path) {
this._sendCommandWithArg(Protocol.RECV, "" + path);
return this._readData();
};
Sync.prototype.end = function() {
this.connection.end();
return this;
};
Sync.prototype.tempFile = function(path) {
return Sync.temp(path);
};
Sync.prototype._writeData = function(stream, timeStamp) {
var readReply, reader, transfer, writeData, writer;
transfer = new PushTransfer;
writeData = (function(_this) {
return function() {
var endListener, errorListener, readableListener, resolver, track, waitForDrain, writeNext, writer;
resolver = Promise.defer();
writer = Promise.resolve().cancellable();
stream.on('end', endListener = function() {
return writer.then(function() {
_this._sendCommandWithLength(Protocol.DONE, timeStamp);
return resolver.resolve();
});
});
waitForDrain = function() {
var drainListener;
resolver = Promise.defer();
_this.connection.on('drain', drainListener = function() {
return resolver.resolve();
});
return resolver.promise["finally"](function() {
return _this.connection.removeListener('drain', drainListener);
});
};
track = function() {
return transfer.pop();
};
writeNext = function() {
var chunk;
if (chunk = stream.read(DATA_MAX_LENGTH) || stream.read()) {
_this._sendCommandWithLength(Protocol.DATA, chunk.length);
transfer.push(chunk.length);
if (_this.connection.write(chunk, track)) {
return writeNext();
} else {
return waitForDrain().then(writeNext);
}
} else {
return Promise.resolve();
}
};
stream.on('readable', readableListener = function() {
return writer.then(writeNext);
});
stream.on('error', errorListener = function(err) {
return resolver.reject(err);
});
return resolver.promise["finally"](function() {
stream.removeListener('end', endListener);
stream.removeListener('readable', readableListener);
stream.removeListener('error', errorListener);
return writer.cancel();
});
};
})(this);
readReply = (function(_this) {
return function() {
return _this.parser.readAscii(4).then(function(reply) {
switch (reply) {
case Protocol.OKAY:
return _this.parser.readBytes(4).then(function(zero) {
return true;
});
case Protocol.FAIL:
return _this._readError();
default:
return _this.parser.unexpected(reply, 'OKAY or FAIL');
}
});
};
})(this);
writer = writeData().cancellable()["catch"](Promise.CancellationError, (function(_this) {
return function(err) {
return _this.connection.end();
};
})(this))["catch"](function(err) {
transfer.emit('error', err);
return reader.cancel();
});
reader = readReply().cancellable()["catch"](Promise.CancellationError, function(err) {
return true;
})["catch"](function(err) {
transfer.emit('error', err);
return writer.cancel();
})["finally"](function() {
return transfer.end();
});
transfer.on('cancel', function() {
writer.cancel();
return reader.cancel();
});
return transfer;
};
Sync.prototype._readData = function() {
var cancelListener, readNext, reader, transfer;
transfer = new PullTransfer;
readNext = (function(_this) {
return function() {
return _this.parser.readAscii(4).cancellable().then(function(reply) {
switch (reply) {
case Protocol.DATA:
return _this.parser.readBytes(4).then(function(lengthData) {
var length;
length = lengthData.readUInt32LE(0);
return _this.parser.readByteFlow(length).progressed(function(chunk) {
return transfer.write(chunk);
}).then(function() {
return readNext();
});
});
case Protocol.DONE:
return _this.parser.readBytes(4).then(function(zero) {
return true;
});
case Protocol.FAIL:
return _this._readError();
default:
return _this.parser.unexpected(reply, 'DATA, DONE or FAIL');
}
});
};
})(this);
reader = readNext()["catch"](Promise.CancellationError, (function(_this) {
return function(err) {
return _this.connection.end();
};
})(this))["catch"](function(err) {
return transfer.emit('error', err);
})["finally"](function() {
transfer.removeListener('cancel', cancelListener);
return transfer.end();
});
transfer.on('cancel', cancelListener = function() {
return reader.cancel();
});
return transfer;
};
Sync.prototype._readError = function() {
return this.parser.readBytes(4).then((function(_this) {
return function(zero) {
return _this.parser.readAll().then(function(buf) {
return Promise.reject(new Parser.FailError(buf.toString()));
});
};
})(this));
};
Sync.prototype._sendCommandWithLength = function(cmd, length) {
var payload;
if (cmd !== Protocol.DATA) {
debug(cmd);
}
payload = new Buffer(cmd.length + 4);
payload.write(cmd, 0, cmd.length);
payload.writeUInt32LE(length, cmd.length);
return this.connection.write(payload);
};
Sync.prototype._sendCommandWithArg = function(cmd, arg) {
var payload, pos;
debug("" + cmd + " " + arg);
payload = new Buffer(cmd.length + 4 + arg.length);
pos = 0;
payload.write(cmd, pos, cmd.length);
pos += cmd.length;
payload.writeUInt32LE(arg.length, pos);
pos += 4;
payload.write(arg, pos);
return this.connection.write(payload);
};
Sync.prototype._enoent = function(path) {
var err;
err = new Error("ENOENT, no such file or directory '" + path + "'");
err.errno = 34;
err.code = 'ENOENT';
err.path = path;
return Promise.reject(err);
};
return Sync;
})(EventEmitter);
module.exports = Sync;
}).call(this);