noodlefrenzy/node-amqp10

View on GitHub
lib/types/composite_type.js

Summary

Maintainability
D
1 day
Test Coverage
'use strict';
var util = require('util'),
    DescribedType = require('./described_type'),
    ForcedType = require('./forced_type');

var knownTypes = {};
function convertKnownType(described) {
  if (described === undefined || described === null ||
      (!(described instanceof DescribedType))) {
    return described;
  }

  if (knownTypes.hasOwnProperty(described.descriptor))
    return new knownTypes[described.descriptor](described);
  return undefined;
}

function wrapField(field, value) {
  if (value instanceof ForcedType) return value;
  if (value === undefined || value === null) {
    if (field.mandatory) throw new Error('missing mandatory field: ' + field.name);
    if (field.hasOwnProperty('default')) return wrapField(field, field.default);
    return null;
  }

  // e.g. Connection containerId()
  if (typeof value === 'function') value = value();

  if (field.hasOwnProperty('requires')) {
    if (value instanceof field.requires) return value;
    if (field.hasOwnProperty('type') && field.type !== '*')
      return new ForcedType(field.type, field.requires(value));
    return new field.requires(value);
  }

  if (field.type === '*') return value;

  if (Array.isArray(value)) {
    if (field.multiple === false) {
      throw Error('field ' + field.name + ' does not support multiple values, got ' + JSON.stringify(value));
    }

    if (value.length === 0) return new ForcedType(field.type, null);
    else if (value.length === 1) return new ForcedType(field.type, value[0]);
    else {
      var values = [], _len = value.length;
      for (var i = 0; i < _len; ++i) values.push(wrapField(field, value[i]));
      return new ForcedType('list', values);
    }
  }

  // @todo: this is a cheesy way to determine if its a composite type already
  if (value.toDescribedType && typeof value.toDescribedType === 'function') return value;

  return new ForcedType(field.type, value);
}

function compositeAccessors(index, field) {
  return {
    enumerable: true, configurable: false,
    get: function() {
      var value = (this.value[index] instanceof ForcedType) ? this.value[index].value : this.value[index];
      if (value === undefined && field.hasOwnProperty('default')) return field.default;
      return value;
    },
    set: function(value) { this.value[index] = wrapField(field, value); }
  };
}

function defineComposite(Base, definition) {
  if (definition === undefined) {
    definition = Base;
    Base = Object;
  }

  var Composite = function(fields) {
    fields = fields || {};
    Composite.super_.call(this, fields.channel);     // @todo: this is AMQPFrame specific, get it out of here

    this.value = [];
    var _len = Composite.fields.length, i, field;
    if (fields instanceof DescribedType) {
      var values = fields.value ? fields.value.map(convertKnownType) : [];
      for (i = 0; i < _len; ++i) {
        field = Composite.fields[i];
        this[field.name] = values[i];
      }
    } else {
      for (i = 0; i < _len; ++i) {
        field = Composite.fields[i];
        if (fields && fields.hasOwnProperty(field.name)) {
          this[field.name] = fields[field.name];
        } else if (field.hasOwnProperty('default')) {
          this[field.name] = wrapField(field, field.default);
        } else {
          this[field.name] = null;
        }
      }
    }
  };

  Composite.fields = definition.fields;
  Composite.descriptor = {
    code: definition.code,
    name: 'amqp:' + definition.name + ':list'
  };

  var properties = {};
  for (var i = 0; i < definition.fields.length; i++) {
    var field = definition.fields[i];
    properties[field.name] = compositeAccessors(i, field);
  }

  Composite.super_ = Base;
  Composite.prototype = Object.create(Base.prototype, properties);
  Composite.prototype.toDescribedType = function() {
    return new DescribedType(Composite.descriptor.code, new ForcedType('list', this.value));
  };

  Composite.prototype.inspect = function(depth) {
    var result = '@' + definition.name + '(' + definition.code + ')';
    var _len = definition.fields.length, values = [];
    for (var i = 0; i < _len; ++i) {
      var field = definition.fields[i];
      values.push(field.name + '=' + util.inspect(this[field.name]));
    }

    result += ' [' + values.join(' ') + ']';
    return result;
  };

  knownTypes[Composite.descriptor.name] = Composite;
  knownTypes[Composite.descriptor.code] = Composite;
  return Composite;
}

module.exports = {
  defineComposite: defineComposite,
  wrapField: wrapField
};