Accepts a raw XLSX file and options that determine how `copytext` should
process it.

@param  {String|Buffer} rawXLSX A Buffer of, or path to, an XLSX file
@param  {Object} [options]
@param  {String} [options.processor='keyvalue'] The processor used on all sheets without overrides
@param  {String[]|String} [options.includeSheets=undefined] Sheets to include
@param  {String[]|String} [options.excludeSheets=undefined] Sheets to exclude
@param  {Object} [options.overrides=undefined] Key value pairs of the sheet name and processor that should be used
@return {Object}
function process (rawXLSX, options) {
  // if no options are passed, set an empty Object as default
  options = options || {}

  // check for errors in the options, and clean up if needed
  options = validateOptions(options)

  // if no processor is set, assume `keyvalue`
  const defaultProcessor = options.processor || 'keyvalue'

  // if no overrides were set, assume there are none
  const overrides = options.overrides || {}

  // if the XLSX file came over as a Buffer, read it differently
  const workbook = rawXLSX instanceof Buffer ? XLSX.read(rawXLSX, {type: 'buffer'}) : XLSX.readFile(rawXLSX)

  // get the names of all the sheets in an Array
  let sheets = workbook.SheetNames

  if (options.includeSheets) {
    sheets = sheets.filter((s) => options.includeSheets.indexOf(s) !== -1)
  }

  if (options.excludeSheets) {
    sheets = sheets.filter((s) => options.excludeSheets.indexOf(s) === -1)
  }

  // the eventual payload returned to the callback
  let payload = {}

  // for each sheet in the workbook process its contents
  sheets.forEach(function (sheet) {
    // determine the appropriate processor, using the override if it exists
    const processor = overrides.hasOwnProperty(sheet) ? overrides[sheet] : defaultProcessor

    // pass pass the sheet on to the process script with the correct processor
    payload[sheet] = getProcessor(processor)(workbook.Sheets[sheet])
  })

  // return the processed data
  return payload
}