168 lines
4.4 KiB
JavaScript
168 lines
4.4 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var codec = require('../lib/object-codec.js');
|
||
|
var bodec = require('bodec');
|
||
|
var inflate = require('../lib/inflate');
|
||
|
var deflate = require('../lib/deflate');
|
||
|
|
||
|
var sha1 = require('git-sha1');
|
||
|
var modes = require('../lib/modes.js');
|
||
|
var db;
|
||
|
|
||
|
mixin.init = init;
|
||
|
|
||
|
mixin.loadAs = loadAs;
|
||
|
mixin.saveAs = saveAs;
|
||
|
mixin.loadRaw = loadRaw;
|
||
|
mixin.saveRaw = saveRaw;
|
||
|
module.exports = mixin;
|
||
|
|
||
|
function mixin(repo, prefix) {
|
||
|
if (!prefix) throw new Error("Prefix required");
|
||
|
repo.refPrefix = prefix;
|
||
|
repo.saveAs = saveAs;
|
||
|
repo.saveRaw = saveRaw;
|
||
|
repo.loadAs = loadAs;
|
||
|
repo.loadRaw = loadRaw;
|
||
|
repo.readRef = readRef;
|
||
|
repo.updateRef = updateRef;
|
||
|
repo.hasHash = hasHash;
|
||
|
}
|
||
|
|
||
|
function init(callback) {
|
||
|
|
||
|
db = openDatabase('tedit', '1.0', 'tedit local data', 10 * 1024 * 1024);
|
||
|
db.transaction(function (tx) {
|
||
|
tx.executeSql(
|
||
|
'CREATE TABLE IF NOT EXISTS objects (hash unique, body blob)'
|
||
|
);
|
||
|
tx.executeSql(
|
||
|
'CREATE TABLE IF NOT EXISTS refs (path unique, value text)'
|
||
|
);
|
||
|
}, function () {
|
||
|
console.error(arguments);
|
||
|
callback(new Error("Problem initializing database"));
|
||
|
}, function () {
|
||
|
callback();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function saveAs(type, body, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
if (!callback) return saveAs.bind(this, type, body);
|
||
|
var hash, buffer;
|
||
|
try {
|
||
|
buffer = codec.frame({type:type,body:body});
|
||
|
hash = sha1(buffer);
|
||
|
}
|
||
|
catch (err) { return callback(err); }
|
||
|
this.saveRaw(hash, buffer, callback);
|
||
|
}
|
||
|
|
||
|
function saveRaw(hash, buffer, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
if (!callback) return saveRaw.bind(this, hash, buffer);
|
||
|
var sql = 'INSERT INTO objects (hash, body) VALUES (?, ?)';
|
||
|
db.transaction(function (tx) {
|
||
|
var text;
|
||
|
try {
|
||
|
text = bodec.toBase64(deflate(buffer));
|
||
|
}
|
||
|
catch (err) {
|
||
|
return callback(err);
|
||
|
}
|
||
|
tx.executeSql(sql, [hash, text], function () {
|
||
|
callback(null, hash);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function loadAs(type, hash, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
if (!callback) return loadAs.bind(this, type, hash);
|
||
|
loadRaw(hash, function (err, buffer) {
|
||
|
if (!buffer) return callback(err);
|
||
|
var parts, body;
|
||
|
try {
|
||
|
parts = codec.deframe(buffer);
|
||
|
if (parts.type !== type) throw new Error("Type mismatch");
|
||
|
body = codec.decoders[type](parts.body);
|
||
|
}
|
||
|
catch (err) {
|
||
|
return callback(err);
|
||
|
}
|
||
|
callback(null, body);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function loadRaw(hash, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
if (!callback) return loadRaw.bind(this, hash);
|
||
|
var sql = 'SELECT * FROM objects WHERE hash=?';
|
||
|
db.readTransaction(function (tx) {
|
||
|
tx.executeSql(sql, [hash], function (tx, result) {
|
||
|
if (!result.rows.length) return callback();
|
||
|
var item = result.rows.item(0);
|
||
|
var buffer;
|
||
|
try {
|
||
|
buffer = inflate(bodec.fromBase64(item.body));
|
||
|
}
|
||
|
catch (err) {
|
||
|
return callback(err);
|
||
|
}
|
||
|
callback(null, buffer);
|
||
|
}, function (tx, error) {
|
||
|
callback(new Error(error.message));
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function hasHash(type, hash, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
loadAs(type, hash, function (err, value) {
|
||
|
if (err) return callback(err);
|
||
|
if (value === undefined) return callback(null, false);
|
||
|
if (type !== "tree") return callback(null, true);
|
||
|
var names = Object.keys(value);
|
||
|
next();
|
||
|
function next() {
|
||
|
if (!names.length) return callback(null, true);
|
||
|
var name = names.pop();
|
||
|
var entry = value[name];
|
||
|
hasHash(modes.toType(entry.mode), entry.hash, function (err, has) {
|
||
|
if (err) return callback(err);
|
||
|
if (has) return next();
|
||
|
callback(null, false);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function readRef(ref, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
var key = this.refPrefix + "/" + ref;
|
||
|
var sql = 'SELECT * FROM refs WHERE path=?';
|
||
|
db.transaction(function (tx) {
|
||
|
tx.executeSql(sql, [key], function (tx, result) {
|
||
|
if (!result.rows.length) return callback();
|
||
|
var item = result.rows.item(0);
|
||
|
callback(null, item.value);
|
||
|
}, function (tx, error) {
|
||
|
callback(new Error(error.message));
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function updateRef(ref, hash, callback) {
|
||
|
/*jshint: validthis: true */
|
||
|
var key = this.refPrefix + "/" + ref;
|
||
|
var sql = 'INSERT INTO refs (path, value) VALUES (?, ?)';
|
||
|
db.transaction(function (tx) {
|
||
|
tx.executeSql(sql, [key, hash], function () {
|
||
|
callback();
|
||
|
}, function (tx, error) {
|
||
|
callback(new Error(error.message));
|
||
|
});
|
||
|
});
|
||
|
}
|