Rewrote argument dict checker to actually make it human readable.
This commit is contained in:
parent
24d9fef94c
commit
d4440e03cc
7 changed files with 200 additions and 90 deletions
|
@ -1,64 +1,180 @@
|
|||
const MamiArguments = (function() {
|
||||
return {
|
||||
type: (name, type, fallback, throwOnFail) => {
|
||||
const MamiArgs = (argName, input, builder) => {
|
||||
if(input !== undefined && typeof input !== 'object')
|
||||
throw `${argName} must be an object or undefined`;
|
||||
if(typeof builder !== 'function')
|
||||
throw 'builder must be a function';
|
||||
|
||||
const args = new Map;
|
||||
|
||||
builder(name => {
|
||||
if(typeof name !== 'string')
|
||||
throw 'name must be a string';
|
||||
if(typeof type !== 'string')
|
||||
throw 'type must be a string';
|
||||
|
||||
return { name: name, type: type, fallback: fallback, throwOnFail: !!throwOnFail };
|
||||
const checkDefined = () => {
|
||||
if(args.has(name))
|
||||
throw `${name} has already been defined`;
|
||||
};
|
||||
checkDefined();
|
||||
|
||||
let created = false;
|
||||
const checkCreated = () => {
|
||||
if(created)
|
||||
throw 'argument has already been defined';
|
||||
};
|
||||
|
||||
const info = {
|
||||
name: name,
|
||||
type: undefined,
|
||||
filter: undefined,
|
||||
constraint: undefined,
|
||||
fallback: undefined,
|
||||
throw: true,
|
||||
required: false,
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
};
|
||||
|
||||
const blueprint = {
|
||||
type: value => {
|
||||
if(typeof value !== 'string' && !Array.isArray(value))
|
||||
throw 'type must be a javascript type or array of valid string values';
|
||||
|
||||
checkCreated();
|
||||
|
||||
info.type = value;
|
||||
return blueprint;
|
||||
},
|
||||
default: value => {
|
||||
checkCreated();
|
||||
info.fallback = value;
|
||||
|
||||
check: (name, fallback, check, throwOnFail) => {
|
||||
if(typeof name !== 'string')
|
||||
throw 'name must be a string';
|
||||
if(typeof check !== 'function')
|
||||
throw 'check must be a function';
|
||||
if(info.type === undefined && info.constraint === undefined)
|
||||
info.type = typeof info.fallback;
|
||||
|
||||
return { name: name, fallback: fallback, check: check, throwOnFail: !!throwOnFail };
|
||||
return blueprint;
|
||||
},
|
||||
|
||||
filter: (name, type, fallback, filter, throwOnFail) => {
|
||||
if(typeof name !== 'string')
|
||||
throw 'name must be a string';
|
||||
if(typeof type !== 'string')
|
||||
throw 'type must be a string';
|
||||
if(typeof filter !== 'function')
|
||||
filter: value => {
|
||||
if(typeof value !== 'function')
|
||||
throw 'filter must be a function';
|
||||
|
||||
return { name: name, type: type, fallback: fallback, filter: filter, throwOnFail: !!throwOnFail };
|
||||
checkCreated();
|
||||
|
||||
info.filter = value;
|
||||
return blueprint;
|
||||
},
|
||||
constraint: value => {
|
||||
if(typeof value !== 'function')
|
||||
throw 'constraint must be a function';
|
||||
|
||||
verify: (args, options) => {
|
||||
if(!Array.isArray(options))
|
||||
throw 'options must be an array';
|
||||
if(typeof args !== 'object')
|
||||
args = {};
|
||||
checkCreated();
|
||||
|
||||
for(const option of options) {
|
||||
if(typeof option !== 'object')
|
||||
throw 'entries of options must be objects';
|
||||
if(!('name' in option) || typeof option.name !== 'string')
|
||||
throw 'option.name must be a string';
|
||||
const name = option.name;
|
||||
const type = 'type' in option && typeof option.type === 'string' ? option.type : undefined;
|
||||
const fallback = 'fallback' in option ? option.fallback : undefined;
|
||||
const check = 'check' in option && typeof option.check === 'function' ? option.check : undefined;
|
||||
const filter = 'filter' in option && typeof option.filter === 'function' ? option.filter : undefined;
|
||||
const throwOnFail = 'throwOnFail' in option && option.throwOnFail;
|
||||
info.constraint = value;
|
||||
return blueprint;
|
||||
},
|
||||
throw: value => {
|
||||
checkCreated();
|
||||
info.throw = value === undefined || value === true;
|
||||
return blueprint;
|
||||
},
|
||||
required: value => {
|
||||
checkCreated();
|
||||
info.required = value === undefined || value === true;
|
||||
return blueprint;
|
||||
},
|
||||
min: value => {
|
||||
checkCreated();
|
||||
if(typeof value !== 'number')
|
||||
throw 'value must be a number';
|
||||
|
||||
if(!(name in args)
|
||||
|| (type !== undefined && typeof args[name] !== type)
|
||||
|| (check !== undefined && !check(args[name]))) {
|
||||
if(throwOnFail)
|
||||
throw `${name} is invalid`;
|
||||
info.min = value;
|
||||
return blueprint;
|
||||
},
|
||||
max: value => {
|
||||
checkCreated();
|
||||
if(typeof value !== 'number')
|
||||
throw 'value must be a number';
|
||||
|
||||
args[name] = fallback;
|
||||
} else if(filter !== undefined)
|
||||
args[name] = filter(args[name]);
|
||||
}
|
||||
info.max = value;
|
||||
return blueprint;
|
||||
},
|
||||
done: () => {
|
||||
checkCreated();
|
||||
checkDefined();
|
||||
|
||||
return args;
|
||||
args.set(name, info);
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
return blueprint;
|
||||
});
|
||||
|
||||
const inputIsNull = input === null;
|
||||
if(inputIsNull)
|
||||
input = {};
|
||||
|
||||
const output = {};
|
||||
|
||||
for(const [name, info] of args) {
|
||||
let value = info.fallback;
|
||||
|
||||
if(info.name in input) {
|
||||
const defaultOrThrow = ex => {
|
||||
if(info.throw)
|
||||
throw ex;
|
||||
value = info.fallback;
|
||||
};
|
||||
|
||||
value = input[info.name];
|
||||
|
||||
if(info.type !== undefined) {
|
||||
if(Array.isArray(info.type)) {
|
||||
if(!info.type.includes(value))
|
||||
defaultOrThrow(`${info.name} must match an enum value`);
|
||||
} else {
|
||||
const type = typeof value;
|
||||
let resolved = false;
|
||||
|
||||
if(type !== info.type) {
|
||||
if(type === 'string') {
|
||||
if(info.type === 'number') {
|
||||
value = parseFloat(value);
|
||||
resolved = true;
|
||||
|
||||
if(info.min !== undefined && value < info.min)
|
||||
value = info.min;
|
||||
else if(info.max !== undefined && value > info.max)
|
||||
value = info.max;
|
||||
} else if(info.type === 'boolean') {
|
||||
value = !!value;
|
||||
resolved = true;
|
||||
}
|
||||
} else if(info.type === 'string') {
|
||||
value = value.toString();
|
||||
resolved = true;
|
||||
}
|
||||
} else resolved = true;
|
||||
|
||||
if(!resolved)
|
||||
defaultOrThrow(`${info.name} must be of type ${info.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
if(info.constraint !== undefined && !info.constraint(value))
|
||||
defaultOrThrow(`${info.name} did not fit within constraints`);
|
||||
|
||||
// you could have a devious streak with this one cuz the checks aren't rerun
|
||||
// but surely you wouldn't fuck yourself over like that
|
||||
if(info.filter !== undefined)
|
||||
value = info.filter(value);
|
||||
} else if(info.required) {
|
||||
if(inputIsNull)
|
||||
throw `${argName} must be a non-null object`;
|
||||
|
||||
throw `${argName} is missing required key ${info.name}`;
|
||||
}
|
||||
|
||||
output[info.name] = value;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
|
|
@ -9,18 +9,18 @@
|
|||
#include colpick/vraw.jsx
|
||||
|
||||
const MamiColourPicker = function(options) {
|
||||
options = MamiArguments.verify(options, [
|
||||
MamiArguments.filter('colour', 'number', 0, num => Math.min(0xFFFFFF, Math.max(0, num))),
|
||||
MamiArguments.type('posX', 'number', -1),
|
||||
MamiArguments.type('posY', 'number', -1),
|
||||
MamiArguments.type('presets', 'object', []),
|
||||
MamiArguments.type('showPresetsTab', 'boolean', options.presets.length > 0),
|
||||
MamiArguments.type('showGridTab', 'boolean', true),
|
||||
MamiArguments.type('showSlidersTab', 'boolean', true),
|
||||
MamiArguments.type('showHexValue', 'boolean', true),
|
||||
MamiArguments.type('showRawValue', 'boolean', true),
|
||||
MamiArguments.type('showDialogButtons', 'boolean', true),
|
||||
]);
|
||||
options = MamiArgs('options', options, define => {
|
||||
define('colour').default(0).filter(value => Math.min(0xFFFFFF, Math.max(0, value))).done();
|
||||
define('posX').default(-1).done();
|
||||
define('posY').default(-1).done();
|
||||
define('presets').default([]).constraint(value => Array.isArray(value)).done();
|
||||
define('showPresetsTab').default(true).done();
|
||||
define('showGridTab').default(true).done();
|
||||
define('showSlidersTab').default(true).done();
|
||||
define('showHexValue').default(true).done();
|
||||
define('showRawValue').default(true).done();
|
||||
define('showDialogButtons').default(true).done();
|
||||
});
|
||||
|
||||
let colour;
|
||||
const needsColour = [];
|
||||
|
@ -115,7 +115,7 @@ const MamiColourPicker = function(options) {
|
|||
},
|
||||
});
|
||||
|
||||
if(options.showPresetsTab)
|
||||
if(options.showPresetsTab && options.presets.length > 0)
|
||||
tabs.add(new MamiColourPickerPresetsTab(options.presets));
|
||||
if(options.showGridTab)
|
||||
tabs.add(new MamiColourPickerGridTab);
|
||||
|
|
|
@ -238,9 +238,9 @@ const MamiMessageBoxDialog = function(info) {
|
|||
};
|
||||
|
||||
const MamiMessageBoxControl = function(options) {
|
||||
options = MamiArguments.verify(options, [
|
||||
MamiArguments.check('parent', undefined, value => value instanceof Element, true),
|
||||
]);
|
||||
options = MamiArgs('options', options, define => {
|
||||
define('parent').required().constraint(value => value instanceof Element).done();
|
||||
});
|
||||
|
||||
const parent = options.parent;
|
||||
const container = new MamiMessageBoxContainer;
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
#include uniqstr.js
|
||||
|
||||
const MamiTabsControl = function(options) {
|
||||
options = MamiArguments.verify(options, [
|
||||
MamiArguments.type('onAdd', 'function', undefined, true),
|
||||
MamiArguments.type('onRemove', 'function', undefined, true),
|
||||
MamiArguments.type('onSwitch', 'function', undefined, true),
|
||||
]);
|
||||
options = MamiArgs('options', options, define => {
|
||||
define('onAdd').required().type('function').done();
|
||||
define('onRemove').required().type('function').done();
|
||||
define('onSwitch').required().type('function').done();
|
||||
});
|
||||
|
||||
const onAdd = options.onAdd,
|
||||
onRemove = options.onRemove,
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#include utility.js
|
||||
|
||||
const MamiViewsControl = function(options) {
|
||||
options = MamiArguments.verify(options, [
|
||||
MamiArguments.check('body', undefined, value => value instanceof Element, true),
|
||||
]);
|
||||
options = MamiArgs('options', options, define => {
|
||||
define('body').required().constraint(value => value instanceof Element).done();
|
||||
});
|
||||
|
||||
const targetBody = options.body;
|
||||
targetBody.classList.toggle('views', true);
|
||||
|
|
|
@ -146,7 +146,7 @@ const MamiSettings = function(storageOrPrefix, eventTarget) {
|
|||
const pub = {
|
||||
type: value => {
|
||||
if(typeof value !== 'string' && !Array.isArray(value))
|
||||
throw 'type must be a javascript type or array of valid string values.';
|
||||
throw 'type must be a javascript type or array of valid string values';
|
||||
|
||||
checkCreated();
|
||||
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
#include args.js
|
||||
|
||||
const MamiWindowTitle = function(options) {
|
||||
options = MamiArguments.verify(options, [
|
||||
MamiArguments.type('getName', 'function', () => ''),
|
||||
MamiArguments.type('setTitle', 'function', text => document.title = text),
|
||||
MamiArguments.check('strobeInterval', 500, val => {
|
||||
const valType = typeof val;
|
||||
return val === 'number' || val === 'function';
|
||||
}),
|
||||
MamiArguments.check('strobeRepeat', 5, val => {
|
||||
const valType = typeof val;
|
||||
return val === 'number' || val === 'function';
|
||||
}),
|
||||
]);
|
||||
options = MamiArgs('options', options, define => {
|
||||
define('getName').default(() => {}).done();
|
||||
define('setTitle').default(text => { document.title = text; }).done();
|
||||
define('strobeInterval').constraint(value => typeof value === 'number' || typeof value === 'function').default(500).done();
|
||||
define('strobeRepeat').constraint(value => typeof value === 'number' || typeof value === 'function').default(5).done();
|
||||
});
|
||||
|
||||
const getName = options.getName;
|
||||
|
||||
|
|
Loading…
Reference in a new issue