1'use strict' 2 3const BB = require('bluebird') 4 5const figgyPudding = require('figgy-pudding') 6const fs = require('fs') 7const index = require('./lib/entry-index') 8const memo = require('./lib/memoization') 9const pipe = require('mississippi').pipe 10const pipeline = require('mississippi').pipeline 11const read = require('./lib/content/read') 12const through = require('mississippi').through 13 14const GetOpts = figgyPudding({ 15 integrity: {}, 16 memoize: {}, 17 size: {} 18}) 19 20module.exports = function get (cache, key, opts) { 21 return getData(false, cache, key, opts) 22} 23module.exports.byDigest = function getByDigest (cache, digest, opts) { 24 return getData(true, cache, digest, opts) 25} 26function getData (byDigest, cache, key, opts) { 27 opts = GetOpts(opts) 28 const memoized = ( 29 byDigest 30 ? memo.get.byDigest(cache, key, opts) 31 : memo.get(cache, key, opts) 32 ) 33 if (memoized && opts.memoize !== false) { 34 return BB.resolve(byDigest ? memoized : { 35 metadata: memoized.entry.metadata, 36 data: memoized.data, 37 integrity: memoized.entry.integrity, 38 size: memoized.entry.size 39 }) 40 } 41 return ( 42 byDigest ? BB.resolve(null) : index.find(cache, key, opts) 43 ).then(entry => { 44 if (!entry && !byDigest) { 45 throw new index.NotFoundError(cache, key) 46 } 47 return read(cache, byDigest ? key : entry.integrity, { 48 integrity: opts.integrity, 49 size: opts.size 50 }).then(data => byDigest ? data : { 51 metadata: entry.metadata, 52 data: data, 53 size: entry.size, 54 integrity: entry.integrity 55 }).then(res => { 56 if (opts.memoize && byDigest) { 57 memo.put.byDigest(cache, key, res, opts) 58 } else if (opts.memoize) { 59 memo.put(cache, entry, res.data, opts) 60 } 61 return res 62 }) 63 }) 64} 65 66module.exports.sync = function get (cache, key, opts) { 67 return getDataSync(false, cache, key, opts) 68} 69module.exports.sync.byDigest = function getByDigest (cache, digest, opts) { 70 return getDataSync(true, cache, digest, opts) 71} 72function getDataSync (byDigest, cache, key, opts) { 73 opts = GetOpts(opts) 74 const memoized = ( 75 byDigest 76 ? memo.get.byDigest(cache, key, opts) 77 : memo.get(cache, key, opts) 78 ) 79 if (memoized && opts.memoize !== false) { 80 return byDigest ? memoized : { 81 metadata: memoized.entry.metadata, 82 data: memoized.data, 83 integrity: memoized.entry.integrity, 84 size: memoized.entry.size 85 } 86 } 87 const entry = !byDigest && index.find.sync(cache, key, opts) 88 if (!entry && !byDigest) { 89 throw new index.NotFoundError(cache, key) 90 } 91 const data = read.sync( 92 cache, 93 byDigest ? key : entry.integrity, 94 { 95 integrity: opts.integrity, 96 size: opts.size 97 } 98 ) 99 const res = byDigest 100 ? data 101 : { 102 metadata: entry.metadata, 103 data: data, 104 size: entry.size, 105 integrity: entry.integrity 106 } 107 if (opts.memoize && byDigest) { 108 memo.put.byDigest(cache, key, res, opts) 109 } else if (opts.memoize) { 110 memo.put(cache, entry, res.data, opts) 111 } 112 return res 113} 114 115module.exports.stream = getStream 116function getStream (cache, key, opts) { 117 opts = GetOpts(opts) 118 let stream = through() 119 const memoized = memo.get(cache, key, opts) 120 if (memoized && opts.memoize !== false) { 121 stream.on('newListener', function (ev, cb) { 122 ev === 'metadata' && cb(memoized.entry.metadata) 123 ev === 'integrity' && cb(memoized.entry.integrity) 124 ev === 'size' && cb(memoized.entry.size) 125 }) 126 stream.write(memoized.data, () => stream.end()) 127 return stream 128 } 129 index.find(cache, key).then(entry => { 130 if (!entry) { 131 return stream.emit( 132 'error', new index.NotFoundError(cache, key) 133 ) 134 } 135 let memoStream 136 if (opts.memoize) { 137 let memoData = [] 138 let memoLength = 0 139 memoStream = through((c, en, cb) => { 140 memoData && memoData.push(c) 141 memoLength += c.length 142 cb(null, c, en) 143 }, cb => { 144 memoData && memo.put(cache, entry, Buffer.concat(memoData, memoLength), opts) 145 cb() 146 }) 147 } else { 148 memoStream = through() 149 } 150 stream.emit('metadata', entry.metadata) 151 stream.emit('integrity', entry.integrity) 152 stream.emit('size', entry.size) 153 stream.on('newListener', function (ev, cb) { 154 ev === 'metadata' && cb(entry.metadata) 155 ev === 'integrity' && cb(entry.integrity) 156 ev === 'size' && cb(entry.size) 157 }) 158 pipe( 159 read.readStream(cache, entry.integrity, opts.concat({ 160 size: opts.size == null ? entry.size : opts.size 161 })), 162 memoStream, 163 stream 164 ) 165 }).catch(err => stream.emit('error', err)) 166 return stream 167} 168 169module.exports.stream.byDigest = getStreamDigest 170function getStreamDigest (cache, integrity, opts) { 171 opts = GetOpts(opts) 172 const memoized = memo.get.byDigest(cache, integrity, opts) 173 if (memoized && opts.memoize !== false) { 174 const stream = through() 175 stream.write(memoized, () => stream.end()) 176 return stream 177 } else { 178 let stream = read.readStream(cache, integrity, opts) 179 if (opts.memoize) { 180 let memoData = [] 181 let memoLength = 0 182 const memoStream = through((c, en, cb) => { 183 memoData && memoData.push(c) 184 memoLength += c.length 185 cb(null, c, en) 186 }, cb => { 187 memoData && memo.put.byDigest( 188 cache, 189 integrity, 190 Buffer.concat(memoData, memoLength), 191 opts 192 ) 193 cb() 194 }) 195 stream = pipeline(stream, memoStream) 196 } 197 return stream 198 } 199} 200 201module.exports.info = info 202function info (cache, key, opts) { 203 opts = GetOpts(opts) 204 const memoized = memo.get(cache, key, opts) 205 if (memoized && opts.memoize !== false) { 206 return BB.resolve(memoized.entry) 207 } else { 208 return index.find(cache, key) 209 } 210} 211 212module.exports.hasContent = read.hasContent 213 214module.exports.copy = function cp (cache, key, dest, opts) { 215 return copy(false, cache, key, dest, opts) 216} 217module.exports.copy.byDigest = function cpDigest (cache, digest, dest, opts) { 218 return copy(true, cache, digest, dest, opts) 219} 220function copy (byDigest, cache, key, dest, opts) { 221 opts = GetOpts(opts) 222 if (read.copy) { 223 return ( 224 byDigest ? BB.resolve(null) : index.find(cache, key, opts) 225 ).then(entry => { 226 if (!entry && !byDigest) { 227 throw new index.NotFoundError(cache, key) 228 } 229 return read.copy( 230 cache, byDigest ? key : entry.integrity, dest, opts 231 ).then(() => byDigest ? key : { 232 metadata: entry.metadata, 233 size: entry.size, 234 integrity: entry.integrity 235 }) 236 }) 237 } else { 238 return getData(byDigest, cache, key, opts).then(res => { 239 return fs.writeFileAsync(dest, byDigest ? res : res.data) 240 .then(() => byDigest ? key : { 241 metadata: res.metadata, 242 size: res.size, 243 integrity: res.integrity 244 }) 245 }) 246 } 247} 248