132 lines
3.8 KiB
JavaScript
132 lines
3.8 KiB
JavaScript
var assert = require('./assert');
|
|
var isTypeName = require('./isTypeName');
|
|
var String = require('./String');
|
|
var Function = require('./Function');
|
|
var isBoolean = require('./isBoolean');
|
|
var isObject = require('./isObject');
|
|
var isNil = require('./isNil');
|
|
var create = require('./create');
|
|
var getTypeName = require('./getTypeName');
|
|
var dict = require('./dict');
|
|
var getDefaultInterfaceName = require('./getDefaultInterfaceName');
|
|
var isIdentity = require('./isIdentity');
|
|
var is = require('./is');
|
|
var extend = require('./extend');
|
|
var assign = require('./assign');
|
|
|
|
function extendInterface(mixins, name) {
|
|
return extend(inter, mixins, name);
|
|
}
|
|
|
|
function getOptions(options) {
|
|
if (!isObject(options)) {
|
|
options = isNil(options) ? {} : { name: options };
|
|
}
|
|
if (!options.hasOwnProperty('strict')) {
|
|
options.strict = inter.strict;
|
|
}
|
|
return options;
|
|
}
|
|
|
|
function inter(props, options) {
|
|
|
|
options = getOptions(options);
|
|
var name = options.name;
|
|
var strict = options.strict;
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
assert(dict(String, Function).is(props), function () { return 'Invalid argument props ' + assert.stringify(props) + ' supplied to interface(props, [options]) combinator (expected a dictionary String -> Type)'; });
|
|
assert(isTypeName(name), function () { return 'Invalid argument name ' + assert.stringify(name) + ' supplied to interface(props, [options]) combinator (expected a string)'; });
|
|
assert(isBoolean(strict), function () { return 'Invalid argument strict ' + assert.stringify(strict) + ' supplied to struct(props, [options]) combinator (expected a boolean)'; });
|
|
}
|
|
|
|
var displayName = name || getDefaultInterfaceName(props);
|
|
var identity = Object.keys(props).map(function (prop) { return props[prop]; }).every(isIdentity);
|
|
|
|
function Interface(value, path) {
|
|
|
|
if (process.env.NODE_ENV === 'production') {
|
|
if (identity) {
|
|
return value; // just trust the input if elements must not be hydrated
|
|
}
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
path = path || [displayName];
|
|
assert(!isNil(value), function () { return 'Invalid value ' + value + ' supplied to ' + path.join('/'); });
|
|
// strictness
|
|
if (strict) {
|
|
for (var k in value) {
|
|
assert(props.hasOwnProperty(k), function () { return 'Invalid additional prop "' + k + '" supplied to ' + path.join('/'); });
|
|
}
|
|
}
|
|
}
|
|
|
|
var idempotent = true;
|
|
var ret = identity ? {} : assign({}, value);
|
|
for (var prop in props) {
|
|
var expected = props[prop];
|
|
var actual = value[prop];
|
|
var instance = create(expected, actual, ( process.env.NODE_ENV !== 'production' ? path.concat(prop + ': ' + getTypeName(expected)) : null ));
|
|
idempotent = idempotent && ( actual === instance );
|
|
ret[prop] = instance;
|
|
}
|
|
|
|
if (idempotent) { // implements idempotency
|
|
ret = value;
|
|
}
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
Object.freeze(ret);
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
Interface.meta = {
|
|
kind: 'interface',
|
|
props: props,
|
|
name: name,
|
|
identity: identity,
|
|
strict: strict
|
|
};
|
|
|
|
Interface.displayName = displayName;
|
|
|
|
Interface.is = function (x) {
|
|
if (isNil(x)) {
|
|
return false;
|
|
}
|
|
if (strict) {
|
|
for (var k in x) {
|
|
if (!props.hasOwnProperty(k)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
for (var prop in props) {
|
|
if (!is(x[prop], props[prop])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
Interface.update = function (instance, patch) {
|
|
return Interface(assert.update(instance, patch));
|
|
};
|
|
|
|
Interface.extend = function (xs, name) {
|
|
return extendInterface([Interface].concat(xs), name);
|
|
};
|
|
|
|
return Interface;
|
|
}
|
|
|
|
inter.strict = false;
|
|
inter.getOptions = getOptions;
|
|
inter.getDefaultName = getDefaultInterfaceName;
|
|
inter.extend = extendInterface;
|
|
module.exports = inter;
|