/* Copyright (c) 2013 Rod Vagg, MIT License */ var xtend = require('xtend') , AbstractIterator = require('./abstract-iterator') , AbstractChainedBatch = require('./abstract-chained-batch') function AbstractLevelDOWN (location) { if (!arguments.length || location === undefined) throw new Error('constructor requires at least a location argument') if (typeof location != 'string') throw new Error('constructor requires a location string argument') this.location = location } AbstractLevelDOWN.prototype.open = function (options, callback) { if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('open() requires a callback argument') if (typeof options != 'object') options = {} if (typeof this._open == 'function') return this._open(options, callback) process.nextTick(callback) } AbstractLevelDOWN.prototype.close = function (callback) { if (typeof callback != 'function') throw new Error('close() requires a callback argument') if (typeof this._close == 'function') return this._close(callback) process.nextTick(callback) } AbstractLevelDOWN.prototype.get = function (key, options, callback) { var err if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('get() requires a callback argument') if (err = this._checkKeyValue(key, 'key', this._isBuffer)) return callback(err) if (!this._isBuffer(key)) key = String(key) if (typeof options != 'object') options = {} if (typeof this._get == 'function') return this._get(key, options, callback) process.nextTick(function () { callback(new Error('NotFound')) }) } AbstractLevelDOWN.prototype.put = function (key, value, options, callback) { var err if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('put() requires a callback argument') if (err = this._checkKeyValue(key, 'key', this._isBuffer)) return callback(err) if (err = this._checkKeyValue(value, 'value', this._isBuffer)) return callback(err) if (!this._isBuffer(key)) key = String(key) // coerce value to string in node, don't touch it in browser // (indexeddb can store any JS type) if (!this._isBuffer(value) && !process.browser) value = String(value) if (typeof options != 'object') options = {} if (typeof this._put == 'function') return this._put(key, value, options, callback) process.nextTick(callback) } AbstractLevelDOWN.prototype.del = function (key, options, callback) { var err if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('del() requires a callback argument') if (err = this._checkKeyValue(key, 'key', this._isBuffer)) return callback(err) if (!this._isBuffer(key)) key = String(key) if (typeof options != 'object') options = {} if (typeof this._del == 'function') return this._del(key, options, callback) process.nextTick(callback) } AbstractLevelDOWN.prototype.batch = function (array, options, callback) { if (!arguments.length) return this._chainedBatch() if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('batch(array) requires a callback argument') if (!Array.isArray(array)) return callback(new Error('batch(array) requires an array argument')) if (typeof options != 'object') options = {} var i = 0 , l = array.length , e , err for (; i < l; i++) { e = array[i] if (typeof e != 'object') continue if (err = this._checkKeyValue(e.type, 'type', this._isBuffer)) return callback(err) if (err = this._checkKeyValue(e.key, 'key', this._isBuffer)) return callback(err) if (e.type == 'put') { if (err = this._checkKeyValue(e.value, 'value', this._isBuffer)) return callback(err) } } if (typeof this._batch == 'function') return this._batch(array, options, callback) process.nextTick(callback) } //TODO: remove from here, not a necessary primitive AbstractLevelDOWN.prototype.approximateSize = function (start, end, callback) { if ( start == null || end == null || typeof start == 'function' || typeof end == 'function') { throw new Error('approximateSize() requires valid `start`, `end` and `callback` arguments') } if (typeof callback != 'function') throw new Error('approximateSize() requires a callback argument') if (!this._isBuffer(start)) start = String(start) if (!this._isBuffer(end)) end = String(end) if (typeof this._approximateSize == 'function') return this._approximateSize(start, end, callback) process.nextTick(function () { callback(null, 0) }) } AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) { var self = this options = xtend(options) ;[ 'start', 'end', 'gt', 'gte', 'lt', 'lte' ].forEach(function (o) { if (options[o] && self._isBuffer(options[o]) && options[o].length === 0) delete options[o] }) options.reverse = !!options.reverse // fix `start` so it takes into account gt, gte, lt, lte as appropriate if (options.reverse && options.lt) options.start = options.lt if (options.reverse && options.lte) options.start = options.lte if (!options.reverse && options.gt) options.start = options.gt if (!options.reverse && options.gte) options.start = options.gte if ((options.reverse && options.lt && !options.lte) || (!options.reverse && options.gt && !options.gte)) options.exclusiveStart = true // start should *not* include matching key return options } AbstractLevelDOWN.prototype.iterator = function (options) { if (typeof options != 'object') options = {} options = this._setupIteratorOptions(options) if (typeof this._iterator == 'function') return this._iterator(options) return new AbstractIterator(this) } AbstractLevelDOWN.prototype._chainedBatch = function () { return new AbstractChainedBatch(this) } AbstractLevelDOWN.prototype._isBuffer = function (obj) { return Buffer.isBuffer(obj) } AbstractLevelDOWN.prototype._checkKeyValue = function (obj, type) { if (obj === null || obj === undefined) return new Error(type + ' cannot be `null` or `undefined`') if (this._isBuffer(obj)) { if (obj.length === 0) return new Error(type + ' cannot be an empty Buffer') } else if (String(obj) === '') return new Error(type + ' cannot be an empty String') } module.exports.AbstractLevelDOWN = AbstractLevelDOWN module.exports.AbstractIterator = AbstractIterator module.exports.AbstractChainedBatch = AbstractChainedBatch