UNPKG

level-filesystem

Version:

Full implementation of the fs module on top of leveldb

117 lines (96 loc) 2.9 kB
var path = require('path'); var once = require('once'); var concat = require('concat-stream'); var octal = require('octal') var stat = require('./stat'); var xtend = require('xtend'); var errno = require('./errno'); var ROOT = stat({ type: 'directory', mode: octal(777), size: 4096 }); var normalize = function(key) { key = key[0] === '/' ? key : '/' + key; key = path.normalize(key); if (key === '/') return key; return key[key.length-1] === '/' ? key.slice(0, -1) : key; }; var prefix = function(key) { var depth = key.split('/').length.toString(36); return '0000000000'.slice(depth.length)+depth+key; }; module.exports = function(db) { var that = {}; that.normalize = normalize; that.get = function(key, cb) { key = normalize(key); if (key === '/') return process.nextTick(cb.bind(null, null, ROOT, '/')); db.get(prefix(key), {valueEncoding:'json'}, function(err, doc) { if (err && err.notFound) return cb(errno.ENOENT(key), null, key); if (err) return cb(err, null, key); cb(null, stat(doc), key); }); }; that.writable = function(key, cb) { key = normalize(key); if (key === '/') return process.nextTick(cb.bind(null, errno.EPERM(key))); that.follow(path.dirname(key), function(err, parent) { if (err) return cb(err); if (!parent.isDirectory()) return cb(errno.ENOTDIR(key)); cb(null, key); }); }; that.list = function(key, cb) { key = normalize(key); var start = prefix(key === '/' ? key : key + '/'); var keys = db.createKeyStream({start: start, end: start+'\xff'}); cb = once(cb); keys.on('error', cb); keys.pipe(concat({encoding:'object'}, function(files) { files = files.map(function(file) { return file.split('/').pop(); }); cb(null, files); })); }; var resolve = function(dir, cb) { var root = '/'; var parts = dir.split('/').slice(1); var loop = function() { that.get(path.join(root, parts.shift()), function(err, doc, key) { if (err) return cb(err, doc, dir); root = doc.target || key; if (!parts.length) return cb(null, doc, key); loop(); }); }; loop(); }; that.follow = function(key, cb) { resolve(normalize(key), function loop(err, doc, key) { if (err) return cb(err, null, key); if (doc.target) return that.get(doc.target, loop); cb(null, stat(doc), key); }); }; that.update = function(key, opts, cb) { that.get(key, function(err, doc, key) { if (err) return cb(err); if (key === '/') return cb(errno.EPERM(key)); that.put(key, xtend(doc, opts), cb); }); }; that.put = function(key, opts, cb) { that.writable(key, function(err, key) { if (err) return cb(err); db.put(prefix(key), stat(opts), {valueEncoding:'json'}, cb); }); }; that.del = function(key, cb) { key = normalize(key); if (key === '/') return process.nextTick(cb.bind(null, errno.EPERM(key))); db.del(prefix(key), cb); }; return that; };