blob: 94f31d6cd68ab4cd999e21b504487a5b118bca03 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/**
* @fileoverview Generator of service interface from JavaScript object.
* This interface can optionally include additional information in a
* descriptor object.
* @private
*/
module.exports = Interface;
var types = require('./types');
var vdlsig = require('../gen-vdl/v.io/v23/vdlroot/signature');
var ReflectInterface = require('./reflect-interface');
var vlog = require('../lib/vlog');
// Each argument type is JSValue.
// This can be overriden by specifying types in the description.
var defaultArgType = types.JSVALUE;
// Default to returning a single out arg.
var defaultOutArgs = [
{
type: defaultArgType
}
];
// Streaming default arg description.
var defaultStreamingArg = {
type: defaultArgType
};
function Interface(service, desc) {
if (!(this instanceof Interface)) {
return new Interface(service, desc);
}
if (typeof desc !== 'object') {
desc = {};
}
vdlsig.Interface.call(this);
var reflectInt = new ReflectInterface(service);
copyIfSet(this, desc, ['name', 'pkgPath', 'doc', 'embeds']);
this.methods = [];
var methods = this.methods;
reflectInt.methods.forEach(function(reflectMethod) {
var thisMethod = {
name: reflectMethod.name,
inArgs: reflectMethod.inArgs,
outArgs: defaultOutArgs
};
// Assign default arg type to each inArg.
if (reflectMethod.inArgs) {
thisMethod.inArgs.forEach(function(inArg) {
inArg.type = defaultArgType;
});
}
// Assign default streaming args.
if (reflectMethod.streaming) {
thisMethod.inStream = defaultStreamingArg;
thisMethod.outStream = defaultStreamingArg;
}
if (desc.hasOwnProperty('methods')) {
var foundMethods = desc.methods.filter(function(meth) {
return meth.name === reflectMethod.name;
});
if (foundMethods.length === 0) {
return;
}
if (foundMethods.length !== 1) {
throw new Error('Duplicate method description for method ' +
reflectMethod.name);
}
var descMethod = foundMethods[0];
if (descMethod.hasOwnProperty('inArgs')) {
if (!Array.isArray(descMethod.inArgs)) {
throw new Error('inArgs expected to be an array');
}
var thisArgs = thisMethod.inArgs;
var descArgs = descMethod.inArgs;
if (thisArgs.length === descArgs.length) {
// Copy arg details.
for (var argix = 0; argix < thisArgs.length; argix++) {
copyIfSet(thisArgs[argix], descArgs[argix],
['doc', 'type', 'name']);
}
} else {
// TODO(bprosnitz) What about methods that use the
// arguments variable and don't declare arguments.
// TODO(bprosnitz) How would this look if we support vararg
// in the future?
vlog.logger.warn('Args of method ' + thisMethod.name + ' don\'t ' +
'match descriptor');
}
}
copyIfSet(thisMethod, descMethod, ['doc', 'outArgs', 'tags']);
if (reflectMethod.streaming === true) {
copyIfSet(thisMethod, descMethod, ['inStream', 'outStream']);
}
// Only add the method if it is in the desc passed in.
methods.push(new vdlsig.Method(thisMethod));
}
});
}
Interface.prototype = new vdlsig.Interface();
function copyIfSet(dst, src, fields) {
for (var i = 0; i < fields.length; i++) {
var fieldName = fields[i];
if (src.hasOwnProperty(fieldName)) {
dst[fieldName] = src[fieldName];
}
}
}