Files

118 lines
2.4 KiB
JavaScript

const constants = require('./constants')
const errors = require('./errors')
const Version = require('./version')
const Comparator = require('./comparator')
const Range = module.exports = exports = 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
}
}
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 }
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)
}