'use strict' const BB = require('bluebird') const figgyPudding = require('figgy-pudding') const fs = require('fs') const index = require('./lib/entry-index') const memo = require('./lib/memoization') const pipe = require('mississippi').pipe const pipeline = require('mississippi').pipeline const read = require('./lib/content/read') const through = require('mississippi').through const GetOpts = figgyPudding({ integrity: {}, memoize: {}, size: {} }) module.exports = function get (cache, key, opts) { return getData(false, cache, key, opts) } module.exports.byDigest = function getByDigest (cache, digest, opts) { return getData(true, cache, digest, opts) } function getData (byDigest, cache, key, opts) { opts = GetOpts(opts) const memoized = ( byDigest ? memo.get.byDigest(cache, key, opts) : memo.get(cache, key, opts) ) if (memoized && opts.memoize !== false) { return BB.resolve(byDigest ? memoized : { metadata: memoized.entry.metadata, data: memoized.data, integrity: memoized.entry.integrity, size: memoized.entry.size }) } return ( byDigest ? BB.resolve(null) : index.find(cache, key, opts) ).then(entry => { if (!entry && !byDigest) { throw new index.NotFoundError(cache, key) } return read(cache, byDigest ? key : entry.integrity, { integrity: opts.integrity, size: opts.size }).then(data => byDigest ? data : { metadata: entry.metadata, data: data, size: entry.size, integrity: entry.integrity }).then(res => { if (opts.memoize && byDigest) { memo.put.byDigest(cache, key, res, opts) } else if (opts.memoize) { memo.put(cache, entry, res.data, opts) } return res }) }) } module.exports.sync = function get (cache, key, opts) { return getDataSync(false, cache, key, opts) } module.exports.sync.byDigest = function getByDigest (cache, digest, opts) { return getDataSync(true, cache, digest, opts) } function getDataSync (byDigest, cache, key, opts) { opts = GetOpts(opts) const memoized = ( byDigest ? memo.get.byDigest(cache, key, opts) : memo.get(cache, key, opts) ) if (memoized && opts.memoize !== false) { return byDigest ? memoized : { metadata: memoized.entry.metadata, data: memoized.data, integrity: memoized.entry.integrity, size: memoized.entry.size } } const entry = !byDigest && index.find.sync(cache, key, opts) if (!entry && !byDigest) { throw new index.NotFoundError(cache, key) } const data = read.sync( cache, byDigest ? key : entry.integrity, { integrity: opts.integrity, size: opts.size } ) const res = byDigest ? data : { metadata: entry.metadata, data: data, size: entry.size, integrity: entry.integrity } if (opts.memoize && byDigest) { memo.put.byDigest(cache, key, res, opts) } else if (opts.memoize) { memo.put(cache, entry, res.data, opts) } return res } module.exports.stream = getStream function getStream (cache, key, opts) { opts = GetOpts(opts) let stream = through() const memoized = memo.get(cache, key, opts) if (memoized && opts.memoize !== false) { stream.on('newListener', function (ev, cb) { ev === 'metadata' && cb(memoized.entry.metadata) ev === 'integrity' && cb(memoized.entry.integrity) ev === 'size' && cb(memoized.entry.size) }) stream.write(memoized.data, () => stream.end()) return stream } index.find(cache, key).then(entry => { if (!entry) { return stream.emit( 'error', new index.NotFoundError(cache, key) ) } let memoStream if (opts.memoize) { let memoData = [] let memoLength = 0 memoStream = through((c, en, cb) => { memoData && memoData.push(c) memoLength += c.length cb(null, c, en) }, cb => { memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength), opts) cb() }) } else { memoStream = through() } stream.emit('metadata', entry.metadata) stream.emit('integrity', entry.integrity) stream.emit('size', entry.size) stream.on('newListener', function (ev, cb) { ev === 'metadata' && cb(entry.metadata) ev === 'integrity' && cb(entry.integrity) ev === 'size' && cb(entry.size) }) pipe( read.readStream(cache, entry.integrity, opts.concat({ size: opts.size == null ? entry.size : opts.size })), memoStream, stream ) }).catch(err => stream.emit('error', err)) return stream } module.exports.stream.byDigest = getStreamDigest function getStreamDigest (cache, integrity, opts) { opts = GetOpts(opts) const memoized = memo.get.byDigest(cache, integrity, opts) if (memoized && opts.memoize !== false) { const stream = through() stream.write(memoized, () => stream.end()) return stream } else { let stream = read.readStream(cache, integrity, opts) if (opts.memoize) { let memoData = [] let memoLength = 0 const memoStream = through((c, en, cb) => { memoData && memoData.push(c) memoLength += c.length cb(null, c, en) }, cb => { memoData && memo.put.byDigest( cache, integrity, Buffer.concat(memoData, memoLength), opts ) cb() }) stream = pipeline(stream, memoStream) } return stream } } module.exports.info = info function info (cache, key, opts) { opts = GetOpts(opts) const memoized = memo.get(cache, key, opts) if (memoized && opts.memoize !== false) { return BB.resolve(memoized.entry) } else { return index.find(cache, key) } } module.exports.hasContent = read.hasContent module.exports.copy = function cp (cache, key, dest, opts) { return copy(false, cache, key, dest, opts) } module.exports.copy.byDigest = function cpDigest (cache, digest, dest, opts) { return copy(true, cache, digest, dest, opts) } function copy (byDigest, cache, key, dest, opts) { opts = GetOpts(opts) if (read.copy) { return ( byDigest ? BB.resolve(null) : index.find(cache, key, opts) ).then(entry => { if (!entry && !byDigest) { throw new index.NotFoundError(cache, key) } return read.copy( cache, byDigest ? key : entry.integrity, dest, opts ).then(() => byDigest ? key : { metadata: entry.metadata, size: entry.size, integrity: entry.integrity }) }) } else { return getData(byDigest, cache, key, opts).then(res => { return fs.writeFileAsync(dest, byDigest ? res : res.data) .then(() => byDigest ? key : { metadata: res.metadata, size: res.size, integrity: res.integrity }) }) } }