/*eslint no-fallthrough: 0*/
(function(glesea) {
/**
Recursively looks through an object to see if its values match the rules
Rules:
undefined -> Can be any type
0 -> Has to be a Number
'' -> Has to be a String
'aaa\bbbb\bccc' -> Has to be enum('aaa', 'bbb', 'ccc')
new Date(0) -> Has to be a Date
{} -> Has to be an object
[0] -> Has to be an Array of Numbers
[''] -> Has to be an Array of Strings
[new Date(0)] -> Has to be an Array of Dates
[[0]] -> Has to be an Array of Arrays of Numbers
{ a: 0 } -> Has to be an Object with key of a: Number
Example:
var r = {
a: 0,
b: '',
c: {
d: [0],
e: undefined
}
}
var t = {
a: 100,
b: 'Monday',
c: {
d: [1,2,3],
e: 'Friday'
}
}
var o = glesea.validate(r, t);
// o ==> [ true, [] ]
If the target didn't match the target, it will return false and a path to the failing entry:
var r = {
a: 0,
b: '',
c: {
d: [0],
e: undefined
}
}
var t = {
a: 100,
b: 'Monday',
c: {
d: ['a', 'b', 'c'],
e: 'Friday'
}
}
var o = glesea.validate(r, t);
// o ==> [ false, [ 'c', 'd', 0 ] ]
@method validate
@for glesea
@param {Object} ruleVal An Object representing the rules
@param {Object} query An Object that will be evaluated against the rules
@return {Array} 0th entry is a Boolean, 1st entry is Array which represents path of potential failing entry
**/
glesea.validate = function(ruleVal, tgtVal) {
return dfs(ruleVal, tgtVal, []);
};
var dfs = function(rule, tgt, stack) {
if (rule === undefined) {
return [true, stack];
}
switch (typeof(rule)) {
case 'object':
if (rule instanceof Date) {
return [(fetch(tgt, stack) instanceof Date), stack];
} else if (Array.isArray(rule)) {
var newTgt = fetch(tgt, stack),
i,
newStack,
result;
if (!Array.isArray(newTgt)) return [false, stack];
for (i = 0; i < newTgt.length; i++) {
newStack = stack.concat([i]);
result = dfs(rule[0], tgt, newStack);
if (!result[0]) return [false, result[1]];
}
return [true, stack];
} else {
for (i in rule) {
newStack = stack.concat([i]);
result = dfs(rule[i], tgt, newStack);
if (!result[0]) return [false, result[1]];
}
return [true, stack];
}
case 'number':
return [(typeof(fetch(tgt, stack)) === 'number'), stack];
case 'boolean':
return [(typeof(fetch(tgt, stack)) === 'boolean'), stack];
case 'string':
if (rule === '') {
return [(typeof(fetch(tgt, stack)) === 'string'), stack];
} else {
var enumRule = rule.split('\b');
return [(enumRule.indexOf(fetch(tgt, stack)) !== -1), stack];
}
}
};
var fetch = function(tgt, stack) {
var i;
if (stack.length === 0) return;
for (i = 0; i < stack.length - 1; i++) {
tgt = tgt[stack[i]];
}
return tgt[stack[i]];
};
})(glesea);