Rewrote argument dict checker to actually make it human readable.

This commit is contained in:
flash 2024-04-23 21:43:08 +00:00
parent 24d9fef94c
commit d4440e03cc
7 changed files with 200 additions and 90 deletions

View file

@ -1,64 +1,180 @@
const MamiArguments = (function() { const MamiArgs = (argName, input, builder) => {
return { if(input !== undefined && typeof input !== 'object')
type: (name, type, fallback, throwOnFail) => { throw `${argName} must be an object or undefined`;
if(typeof name !== 'string') if(typeof builder !== 'function')
throw 'name must be a string'; throw 'builder must be a function';
if(typeof type !== 'string')
throw 'type must be a string';
return { name: name, type: type, fallback: fallback, throwOnFail: !!throwOnFail }; const args = new Map;
},
check: (name, fallback, check, throwOnFail) => { builder(name => {
if(typeof name !== 'string') if(typeof name !== 'string')
throw 'name must be a string'; throw 'name must be a string';
if(typeof check !== 'function')
throw 'check must be a function';
return { name: name, fallback: fallback, check: check, throwOnFail: !!throwOnFail }; const checkDefined = () => {
}, if(args.has(name))
throw `${name} has already been defined`;
};
checkDefined();
filter: (name, type, fallback, filter, throwOnFail) => { let created = false;
if(typeof name !== 'string') const checkCreated = () => {
throw 'name must be a string'; if(created)
if(typeof type !== 'string') throw 'argument has already been defined';
throw 'type must be a string'; };
if(typeof filter !== 'function')
throw 'filter must be a function';
return { name: name, type: type, fallback: fallback, filter: filter, throwOnFail: !!throwOnFail }; const info = {
}, name: name,
type: undefined,
filter: undefined,
constraint: undefined,
fallback: undefined,
throw: true,
required: false,
min: undefined,
max: undefined,
};
verify: (args, options) => { const blueprint = {
if(!Array.isArray(options)) type: value => {
throw 'options must be an array'; if(typeof value !== 'string' && !Array.isArray(value))
if(typeof args !== 'object') throw 'type must be a javascript type or array of valid string values';
args = {};
for(const option of options) { checkCreated();
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;
if(!(name in args) info.type = value;
|| (type !== undefined && typeof args[name] !== type) return blueprint;
|| (check !== undefined && !check(args[name]))) { },
if(throwOnFail) default: value => {
throw `${name} is invalid`; checkCreated();
info.fallback = value;
args[name] = fallback; if(info.type === undefined && info.constraint === undefined)
} else if(filter !== undefined) info.type = typeof info.fallback;
args[name] = filter(args[name]);
return blueprint;
},
filter: value => {
if(typeof value !== 'function')
throw 'filter must be a function';
checkCreated();
info.filter = value;
return blueprint;
},
constraint: value => {
if(typeof value !== 'function')
throw 'constraint must be a function';
checkCreated();
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';
info.min = value;
return blueprint;
},
max: value => {
checkCreated();
if(typeof value !== 'number')
throw 'value must be a number';
info.max = value;
return blueprint;
},
done: () => {
checkCreated();
checkDefined();
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}`);
}
} }
return args; 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;
};

View file

@ -9,18 +9,18 @@
#include colpick/vraw.jsx #include colpick/vraw.jsx
const MamiColourPicker = function(options) { const MamiColourPicker = function(options) {
options = MamiArguments.verify(options, [ options = MamiArgs('options', options, define => {
MamiArguments.filter('colour', 'number', 0, num => Math.min(0xFFFFFF, Math.max(0, num))), define('colour').default(0).filter(value => Math.min(0xFFFFFF, Math.max(0, value))).done();
MamiArguments.type('posX', 'number', -1), define('posX').default(-1).done();
MamiArguments.type('posY', 'number', -1), define('posY').default(-1).done();
MamiArguments.type('presets', 'object', []), define('presets').default([]).constraint(value => Array.isArray(value)).done();
MamiArguments.type('showPresetsTab', 'boolean', options.presets.length > 0), define('showPresetsTab').default(true).done();
MamiArguments.type('showGridTab', 'boolean', true), define('showGridTab').default(true).done();
MamiArguments.type('showSlidersTab', 'boolean', true), define('showSlidersTab').default(true).done();
MamiArguments.type('showHexValue', 'boolean', true), define('showHexValue').default(true).done();
MamiArguments.type('showRawValue', 'boolean', true), define('showRawValue').default(true).done();
MamiArguments.type('showDialogButtons', 'boolean', true), define('showDialogButtons').default(true).done();
]); });
let colour; let colour;
const needsColour = []; 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)); tabs.add(new MamiColourPickerPresetsTab(options.presets));
if(options.showGridTab) if(options.showGridTab)
tabs.add(new MamiColourPickerGridTab); tabs.add(new MamiColourPickerGridTab);

View file

@ -238,9 +238,9 @@ const MamiMessageBoxDialog = function(info) {
}; };
const MamiMessageBoxControl = function(options) { const MamiMessageBoxControl = function(options) {
options = MamiArguments.verify(options, [ options = MamiArgs('options', options, define => {
MamiArguments.check('parent', undefined, value => value instanceof Element, true), define('parent').required().constraint(value => value instanceof Element).done();
]); });
const parent = options.parent; const parent = options.parent;
const container = new MamiMessageBoxContainer; const container = new MamiMessageBoxContainer;

View file

@ -2,11 +2,11 @@
#include uniqstr.js #include uniqstr.js
const MamiTabsControl = function(options) { const MamiTabsControl = function(options) {
options = MamiArguments.verify(options, [ options = MamiArgs('options', options, define => {
MamiArguments.type('onAdd', 'function', undefined, true), define('onAdd').required().type('function').done();
MamiArguments.type('onRemove', 'function', undefined, true), define('onRemove').required().type('function').done();
MamiArguments.type('onSwitch', 'function', undefined, true), define('onSwitch').required().type('function').done();
]); });
const onAdd = options.onAdd, const onAdd = options.onAdd,
onRemove = options.onRemove, onRemove = options.onRemove,

View file

@ -2,9 +2,9 @@
#include utility.js #include utility.js
const MamiViewsControl = function(options) { const MamiViewsControl = function(options) {
options = MamiArguments.verify(options, [ options = MamiArgs('options', options, define => {
MamiArguments.check('body', undefined, value => value instanceof Element, true), define('body').required().constraint(value => value instanceof Element).done();
]); });
const targetBody = options.body; const targetBody = options.body;
targetBody.classList.toggle('views', true); targetBody.classList.toggle('views', true);

View file

@ -146,7 +146,7 @@ const MamiSettings = function(storageOrPrefix, eventTarget) {
const pub = { const pub = {
type: value => { type: value => {
if(typeof value !== 'string' && !Array.isArray(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(); checkCreated();

View file

@ -1,18 +1,12 @@
#include args.js #include args.js
const MamiWindowTitle = function(options) { const MamiWindowTitle = function(options) {
options = MamiArguments.verify(options, [ options = MamiArgs('options', options, define => {
MamiArguments.type('getName', 'function', () => ''), define('getName').default(() => {}).done();
MamiArguments.type('setTitle', 'function', text => document.title = text), define('setTitle').default(text => { document.title = text; }).done();
MamiArguments.check('strobeInterval', 500, val => { define('strobeInterval').constraint(value => typeof value === 'number' || typeof value === 'function').default(500).done();
const valType = typeof val; define('strobeRepeat').constraint(value => typeof value === 'number' || typeof value === 'function').default(5).done();
return val === 'number' || val === 'function'; });
}),
MamiArguments.check('strobeRepeat', 5, val => {
const valType = typeof val;
return val === 'number' || val === 'function';
}),
]);
const getName = options.getName; const getName = options.getName;