diff --git a/lib/and.js b/lib/and.js index f18a4d0..43d5af9 100644 --- a/lib/and.js +++ b/lib/and.js @@ -25,10 +25,12 @@ function and(...predicates) { unform: value => unform(predicates[0], value), gen: () => gen(predicates[0]), describe: () => [and.name, ...describe(predicates)], - explain: (value, {via}) => { + explain: function*(value, {via}) { const problems = _.map(predicates, predicate => explainData(predicate, value, {via})); const found = _.find(problems, p => !_.isNull(p)); - return _.isUndefined(found) ? null : {problems: found.problems}; + if (found) { + yield found.problems[0]; + } } }; } diff --git a/lib/def.js b/lib/def.js index 61f2a81..f4f184b 100644 --- a/lib/def.js +++ b/lib/def.js @@ -20,7 +20,11 @@ function createArraySpec(predicate) { return { conform: (value, unwrap = false) => conform(value, unwrap, _.partial(_.includes, predicate)), describe: () => predicate, - explain: value => _.includes(predicate, value) ? null : getExplanation(value), + explain: function*(value) { + if (!_.includes(predicate, value)) { + yield getExplanation(value); + } + }, gen: () => tcg.null.then(() => tcg.return(_.sample(predicate))) }; } @@ -29,9 +33,11 @@ function createFunctionSpec(predicate, gen) { return { conform: (value, unwrap = false) => conform(value, unwrap, predicate), unform: _.identify, - explain: (value, options) => { + explain: function*(value, options) { options.pred = _.isEmpty(predicate.name) || predicate.name === 'anonymous' ? `${predicate}` : predicate.name; - return predicate(value) ? null : getExplanation(value, options); + if (!predicate(value)) { + yield getExplanation(value, options); + } }, gen: gen ? gen : () => functions.gen(predicate) }; @@ -39,13 +45,11 @@ function createFunctionSpec(predicate, gen) { function getExplanation(value, {path, pred = unknownString, via}) { return { - problems: [{ - path, - pred, - val: value, - via, - 'in': [] - }] + path, + pred, + val: value, + via, + 'in': [] }; } diff --git a/lib/explainData.js b/lib/explainData.js index 9a3ede6..0cdb255 100644 --- a/lib/explainData.js +++ b/lib/explainData.js @@ -1,14 +1,15 @@ const _ = require('lodash'); const {spec: specize} = require('./def'); -const {isFunction, isString} = require('./utils'); +const {isString} = require('./utils'); function explainData(spec, value, {path = [], via = []} = {}) { const lookup = specize(spec); if (lookup) { const name = specName(spec); const viaOut = _.isUndefined(name) ? via : [name, ...via]; - return lookup.explain(value, {path, pred: name, via: viaOut}); + const problems = [...lookup.explain(value, {path, pred: name, via: viaOut})]; + return _.isEmpty(problems) ? null : {problems}; } else { throw new Error(`Unable to resolve spec ${spec}`); } diff --git a/lib/intIn.js b/lib/intIn.js index 5c5ee3f..c3b042b 100644 --- a/lib/intIn.js +++ b/lib/intIn.js @@ -10,9 +10,7 @@ function intIn(start, end) { unform: _.identity, gen: () => tcg.intWithin(start, end - 1), describe: () => ['and', 'isInt', ['isIntInRange', start, end]], - explain: (value, {via}) => { - const problems = []; - return _.isEmpty(problems) ? null : {problems}; + explain: function*(value, {via}) { } }; } diff --git a/lib/nilable.js b/lib/nilable.js index 2918095..d2522b2 100644 --- a/lib/nilable.js +++ b/lib/nilable.js @@ -15,20 +15,18 @@ function nilable(predicate) { unform: _.identity, gen: () => tcg.null.then(() => _.random(10) < 2 ? tcg.null : gen(predicate)), describe: () => [nilable.name, ...describe([predicate])], - explain: (value, {via}) => { - return _.isNull(value) || isValid(predicate, value) ? null : - { - problems: [ - explainData(predicate, value, {path: ['pred'], via}).problems[0], - { - path: ['null'], - pred: 'isNull', - val: value, - via, - 'in': [] - } - ] - }; + explain: function*(value, {via}) { + if (!(_.isNull(value) || isValid(predicate, value))) { + yield explainData(predicate, value, {path: ['pred'], via}).problems[0]; + + yield { + path: ['null'], + pred: 'isNull', + val: value, + via, + 'in': [] + } + } } }; } diff --git a/lib/or.js b/lib/or.js index 1b8771f..ecf255b 100644 --- a/lib/or.js +++ b/lib/or.js @@ -25,9 +25,13 @@ function or(...predicates) { }, gen: () => tcg.null.then(() => gen(_.sample(pairs)[1])), describe: () => [or.name, ...describe(predicates)], - explain: (value, {via}) => { + explain: function*(value, {via}) { const problems = _.map(pairs, ([k, predicate]) => explainData(predicate, value, {path: [k], via})); - return _.some(problems, _.isNull) ? null : {problems: _.flatten(_.map(problems, 'problems'))}; + if (!_.some(problems, _.isNull)) { + for (let i = 0; i < problems.length; i++) { + yield problems[i].problems[0]; + } + } } }; } diff --git a/lib/plus.js b/lib/plus.js index f0d1191..f91c372 100644 --- a/lib/plus.js +++ b/lib/plus.js @@ -17,44 +17,39 @@ function plus(spec) { unform: _.identity, gen: () => tcg.null.then(() => tcg.array(gen(spec), {size: _.random(1, 5)})), describe: () => [plus.name, ...describe([spec])], - explain: (values, {via}) => { - const problems = []; - explainEmptyInput(problems, values, via); - if (_.isEmpty(problems)) { - explainValidInput(problems, spec, values, via); - } - return _.isEmpty(problems) ? null : {problems}; + explain: function*(values, {via}) { + yield* explainLength(values, via); + yield* explainInvalid(values, spec, via); } }; } -function explainEmptyInput(problems, values, via) { - if (values.length === 0) { - problems.push({ +function* explainLength(values, via) { + if (_.isEmpty(values)) { + yield { path: [], reason: 'Insufficient input', val: values, via, 'in': [] - }); + }; } - return problems; } -function explainValidInput(problems, spec, values, via) { +function* explainInvalid(values, spec, via) { const index = _.findIndex(values, value => !isValid(spec, value)); if (index !== -1) { const val = values[index]; + console.log('***', explainData(spec, val)); const pred = explainData(spec, val).problems[0].pred; - problems.push({ + yield { path: [], pred, val, via, 'in': [index] - }); + }; } - return problems; } module.exports = plus; diff --git a/lib/tuple.js b/lib/tuple.js index 19109df..20fea19 100644 --- a/lib/tuple.js +++ b/lib/tuple.js @@ -14,15 +14,17 @@ function tuple(...predicates) { unform: _.identity, gen: () => _.map(predicates, gen), describe: () => [tuple.name, ...describe(predicates)], - explain: (value, {via}) => { + explain: function*(value, {via}) { const f = new Function('values', `return values.length === ${predicates.length}`); - return explainPredicate(f, value, {via}); + yield* explainPredicate(f, value, {via}); } }; } -function explainPredicate(pred, value, options) { - return pred(value) ? null : explainData(pred, value, options); +function* explainPredicate(pred, value, options) { + if (!pred(value)) { + yield explainData(pred, value, options).problems[0]; + } } module.exports = tuple;