const constants = require('./constants') const errors = require('./errors') const Version = require('./version') const Comparator = require('./comparator') class Range { constructor(comparators = []) { this.comparators = comparators } test(version) { for (const set of this.comparators) { let matches = true for (const comparator of set) { if (comparator.test(version)) continue matches = false break } if (matches) return true } return false } toString() { let result = '' let first = true for (const set of this.comparators) { if (first) first = false else result += ' || ' result += set.join(' ') } return result } } module.exports = exports = Range exports.parse = function parse(input, state = { position: 0, partial: false }) { let i = state.position let c const unexpected = (expected) => { let msg if (i >= input.length) { msg = `Unexpected end of input in '${input}'` } else { msg = `Unexpected token '${input[i]}' in '${input}' at position ${i}` } if (expected) msg += `, ${expected}` throw errors.INVALID_VERSION(msg, unexpected) } const comparators = [] while (i < input.length) { const set = [] while (i < input.length) { c = input[i] let operator = constants.EQ if (c === '<') { operator = constants.LT c = input[++i] if (c === '=') { operator = constants.LTE c = input[++i] } } else if (c === '>') { operator = constants.GT c = input[++i] if (c === '=') { operator = constants.GTE c = input[++i] } } else if (c === '=') { c = input[++i] } const state = { position: i, partial: true, range: true } set.push(new Comparator(operator, Version.parse(input, state))) c = input[(i = state.position)] while (c === ' ') c = input[++i] if (c === '|' && input[i + 1] === '|') { c = input[(i += 2)] while (c === ' ') c = input[++i] break } if (c && c !== '<' && c !== '>') unexpected("expected '||', '<', or '>'") } if (set.length) comparators.push(set) } if (i < input.length && state.partial === false) { unexpected('expected end of input') } state.position = i return new Range(comparators) }