lib/types/composite_type.js
'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
};