text: /*
   * extruder
   * http://github.com/node-task/extruder
   *
   * Copyright (c) 2012 Tyler Kellen, contributors
   * Licensed under the MIT license.
   */
  
  var when = require('when');
  var array = require('ensure-array');
  
  var SOURCE_ONLY = 'sourceonly';
  var SRC_DEST_PAIRS = 'srcdestpairs';
  
  var extruder = {};
  
  /**
    Determine what type of node-task input object has been
    provided.  Two forms are acceptable:
  
    var input_list = ['path/to/source1', 'path/to/source2'];
  
    var input_pairs = [
      { src: 'path/to/source1', dest: 'path/to/dest' }
      { src: ['path/to/source2'], dest: 'path/to/dest' }
    ];
  */
  var inputType = function(input) {
    if(typeof input[0] === "string") {
      return SOURCE_ONLY;
    } else {
      return SRC_DEST_PAIRS;
    }
  };
  
  /**
    Iterate over a single node-task src/dest input pair and map
    its sources for transformation by a provided callback. Return
    a promise which resolves to the modified src/dest pair.
  */
  var mapPair = function(srcDest, fn) {
    var mapped = array(srcDest.src).map(function (source) {
      return fn(source, srcDest.dest);
    });
    return when.all(mapped).then(function (mappedSource) {
      srcDest.src = mappedSource;
      return srcDest;
    });
  };
  
  /**
    Iterate over a single node-task src/dest input pair, reducing
    it's sources with a provided callback.  Return a promise
    which resolves to the modified src/dest pair.
  */
  var reducePair = function(srcDest, fn) {
    var reduced = fn(array(srcDest.src), srcDest.dest);
    return when(reduced).then(function (reducedSources) {
      srcDest.src = reducedSources;
      return srcDest;
    });
  };
  
  /**
    Iterate over node-task inputs and map their sources for
    transformation by a provided callback. Return a promise
    which resolves to the modified input.
  */
  extruder.map = function (input, fn, context) {
    var mapped;
    if(context) {
      fn = fn.bind(context);
    }
  
    // ensure input is an array
    input = array(input);
  
    if(inputType(input) === SOURCE_ONLY) {
      // if input is only an array of sources, use the callback directly.
      mapped = input.map(function(source) {
        return fn(source);
      });
    } else {
      // if input is src/dest pairs, use the callback on each item
      mapped = input.map(function (group) {
        return mapPair(group, fn);
      });
    }
    return when.all(mapped);
  };
  
  /**
    Iterate over node-task inputs and reduce their sources
    with a provided callback.  Return a promise which resolves
    to the modified input.
  */
  extruder.reduce = function (input, fn, context) {
    var reduced;
    if(context) {
      fn = fn.bind(context);
    }
  
    // ensure input is an array
    input = array(input);
  
    if(inputType(input) === SOURCE_ONLY) {
      // if input is only an array of sources, use the callback directly.
      reduced = fn(input);
      return when(reduced);
    } else {
      // if input is src/dest pairs, use the callback on each group
      reduced = input.map(function (group) {
        return reducePair(group, fn);
      });
      return when.all(reduced);
    }
  };
  
  module.exports = extruder;