126 lines
3.1 KiB
JavaScript
126 lines
3.1 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var modes = require('./modes');
|
||
|
var defer = require('./defer');
|
||
|
|
||
|
// options.encrypt(plain) -> encrypted
|
||
|
// options.decrypt(encrypted) -> plain
|
||
|
// options.shouldEncrypt(path) -> boolean
|
||
|
// options.getRootTree() => hash
|
||
|
// options.setRootTree(hash) =>
|
||
|
module.exports = function (repo, options) {
|
||
|
var toWrite = {};
|
||
|
var callbacks = [];
|
||
|
var writing = false;
|
||
|
|
||
|
return {
|
||
|
readFile: readFile,
|
||
|
writeFile: writeFile,
|
||
|
readDir: readDir
|
||
|
};
|
||
|
|
||
|
function readFile(path, callback) {
|
||
|
if (!callback) return readFile.bind(null, path);
|
||
|
|
||
|
// If there is a pending write for this path, pull from the cache.
|
||
|
if (toWrite[path]) return callback(null, toWrite[path]);
|
||
|
|
||
|
// Otherwise read from the persistent storage
|
||
|
options.getRootTree(onRootTree);
|
||
|
|
||
|
function onRootTree(err, hash) {
|
||
|
if (!hash) return callback(err);
|
||
|
repo.pathToEntry(hash, path, onEntry);
|
||
|
}
|
||
|
|
||
|
function onEntry(err, entry) {
|
||
|
if (!entry || !modes.isBlob(entry.mode)) return callback(err);
|
||
|
|
||
|
repo.loadAs("blob", entry.hash, function (err, content) {
|
||
|
if (!content) return callback(err);
|
||
|
if (entry.mode === modes.sym) {
|
||
|
content = options.decrypt(content);
|
||
|
}
|
||
|
callback(null, content);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function writeFile(path, binary, callback) {
|
||
|
if (!callback) return writeFile.bind(null, path, binary);
|
||
|
toWrite[path] = binary;
|
||
|
callbacks.push(callback);
|
||
|
defer(check);
|
||
|
}
|
||
|
|
||
|
function readDir(path, callback) {
|
||
|
if (!callback) return readDir.bind(null, path);
|
||
|
|
||
|
options.getRootTree(onRootTree);
|
||
|
|
||
|
function onRootTree(err, hash) {
|
||
|
if (!hash) return callback(err);
|
||
|
repo.pathToEntry(hash, path, onEntry);
|
||
|
}
|
||
|
|
||
|
function onEntry(err, entry) {
|
||
|
if (!entry || entry.mode !== modes.tree) return callback(err);
|
||
|
repo.loadAs("tree", entry.hash, onTree);
|
||
|
}
|
||
|
|
||
|
function onTree(err, tree) {
|
||
|
if (!tree) return callback(err);
|
||
|
callback(null, Object.keys(tree));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function check() {
|
||
|
if (writing || !callbacks.length) return;
|
||
|
writing = true;
|
||
|
options.getRootTree(onRootTree);
|
||
|
|
||
|
function onRootTree(err, hash) {
|
||
|
if (err) return callall(err);
|
||
|
var files = pullFiles();
|
||
|
if (hash) files.base = hash;
|
||
|
repo.createTree(files, onNewTree);
|
||
|
}
|
||
|
|
||
|
function onNewTree(err, hash) {
|
||
|
if (err) return callall(err);
|
||
|
options.setRootTree(hash, onSaveRoot);
|
||
|
}
|
||
|
|
||
|
function onSaveRoot(err) {
|
||
|
if (err) return callall(err);
|
||
|
writing = false;
|
||
|
callall();
|
||
|
defer(check);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function pullFiles() {
|
||
|
var files = Object.keys(toWrite).map(function (path) {
|
||
|
var content = toWrite[path];
|
||
|
delete toWrite[path];
|
||
|
var mode = modes.blob;
|
||
|
if (options.shouldEncrypt && options.shouldEncrypt(path)) {
|
||
|
mode = modes.sym;
|
||
|
content = options.encrypt(content);
|
||
|
}
|
||
|
return {
|
||
|
path: path,
|
||
|
mode: mode,
|
||
|
content: content
|
||
|
};
|
||
|
});
|
||
|
return files;
|
||
|
}
|
||
|
|
||
|
function callall(err) {
|
||
|
callbacks.splice(0, callbacks.length).forEach(function (callback) {
|
||
|
callback(err);
|
||
|
});
|
||
|
}
|
||
|
};
|