diff --git a/src/filter.js b/src/filter.js index 397dfd3..ca091b6 100644 --- a/src/filter.js +++ b/src/filter.js @@ -9,9 +9,8 @@ const _false = () => false const _and = arr => p => arr.map(e => e(p)).reduce((a, b) => a && b) const _or = arr => p => arr.map(e => e(p)).reduce((a, b) => a || b) -const similar = (key, reference, name, units) => { +const similar = (keys, reference, name, units) => { let neg = false - if (reference.startsWith('-')) { neg = true reference = reference.slice(1) @@ -20,15 +19,19 @@ const similar = (key, reference, name, units) => { // support both string or regex as reference let internal_tester = val => (''+val) == reference if (reference.startsWith('/')) { - const regex_parts = reference.split('/') - regex_parts.shift() // remove starting slash - const flags = regex_parts.pop() - const regex = new RegExp(regex_parts.join('/'), flags) - internal_tester = val => regex.test(''+val) + try { + const regex_parts = reference.split('/') + regex_parts.shift() // remove starting slash + const flags = regex_parts.pop() + const regex = new RegExp(regex_parts.join('/'), flags) + internal_tester = val => regex.test(''+val) + } catch (ex) { + throw new Error(`Invalid regex "${reference}" found at filter "${name}"!`) + } } // support strings, arrays, or objects as key - const external_tester = point => { + const external_tester = (point, key) => { const value = u.deep(point, key) if (a.type(value)() == 'array') { return value.some(subkey => internal_tester(subkey)) @@ -39,11 +42,12 @@ const similar = (key, reference, name, units) => { } } - // negation happens at the end + // consider negation if (neg) { - return point => !external_tester(point) + return point => keys.every(key => !external_tester(point, key)) + } else { + return point => keys.some(key => external_tester(point, key)) } - return external_tester } const comparators = { @@ -75,7 +79,7 @@ const simple = (exp, name, units) => { value = exp } - return point => keys.some(key => comparators[op](key, value, name, units)(point)) + return point => comparators[op](keys, value, name, units)(point) } const complex = (config, name, units, aggregator=_or) => { @@ -109,7 +113,7 @@ const contains_object = (val) => { } exports.parse = (config, name, points={}, units={}, asym='source') => { - + let result = [] // if a filter decl is undefined, it's just the default point at [0, 0] diff --git a/test/unit/filter.js b/test/unit/filter.js index c040687..3972e93 100644 --- a/test/unit/filter.js +++ b/test/unit/filter.js @@ -49,17 +49,21 @@ describe('Filter', function() { names(filter('/^o/', '', points)).should.deep.equal(['one', 'three', 'mirror_one']) // middle spec, should be the same as above, only explicit names(filter('~ /^o/', '', points)).should.deep.equal(['one', 'three', 'mirror_one']) - // full spec (n would normally match both one and even, but on the tags level, it's just even) + // full spec (/n/ would normally match both "one" and "even", but on the tags level, it's just even) names(filter('meta.tags ~ /n/', '', points)).should.deep.equal(['two']) + names(filter('meta.name,meta.tags ~ /n/', '', points)).should.deep.equal(['one', 'two', 'mirror_one']) // negation names(filter('meta.tags ~ -/n/', '', points)).should.deep.equal(['one', 'three', 'mirror_one']) + names(filter('meta.name,meta.tags ~ -/n/', '', points)).should.deep.equal(['three']) // arrays OR by default at odd levels (including top level)... names(filter(['one', 'two'], '', points)).should.deep.equal(['one', 'two']) // ...and AND at even levels names(filter([['even', 'prime']], '', points)).should.deep.equal(['two']) // arbitrary nesting should be possible names(filter([[['even', 'odd'], 'prime']], '', points)).should.deep.equal(['two', 'three']) - // anything other than string/array/object/undefined is an error + // invalid regexes should throw meaningful errors + filter.bind(this, '/\\/', '', points).should.throw('Invalid regex') + // anything other than string/array/object/undefined is also an error filter.bind(this, 28, '', points).should.throw('Unexpected type') })