import { matches } from "../dependencies.ts";
const starSub = /((\d+\.)*\d+)\.\*/;
function incrementLastNumber(list: number[]) { const newList = [...list]; newList[newList.length - 1]++; return newList;}export function rangeOf(range: string | Checker): Checker { return Checker.parse(range);}
export function rangeAnd(...ranges: (string | Checker)[]): Checker { if (ranges.length === 0) { throw new Error("No ranges given"); } const [firstCheck, ...rest] = ranges; return Checker.parse(firstCheck).and(...rest);}
export function rangeOr(...ranges: (string | Checker)[]): Checker { if (ranges.length === 0) { throw new Error("No ranges given"); } const [firstCheck, ...rest] = ranges; return Checker.parse(firstCheck).or(...rest);}
export function notRange(range: string | Checker): Checker { return rangeOf(range).not();}
export class EmVer { static from(range: string | EmVer): EmVer { if (range instanceof EmVer) { return range; } return EmVer.parse(range); } static parse(range: string): EmVer { const values = range.split(".").map((x) => parseInt(x)); for (const value of values) { if (isNaN(value)) { throw new Error(`Couldn't parse range: ${range}`); } } return new EmVer(values); } private constructor(public readonly values: number[]) {}
public withLastIncremented() { return new EmVer(incrementLastNumber(this.values)); }
public greaterThan(other: EmVer): boolean { for (const i in this.values) { if (other.values[i] == null) { return true; } if (this.values[i] > other.values[i]) { return true; }
if (this.values[i] < other.values[i]) { return false; } } return false; }
public equals(other: EmVer): boolean { if (other.values.length !== this.values.length) { return false; } for (const i in this.values) { if (this.values[i] !== other.values[i]) { return false; } } return true; } public greaterThanOrEqual(other: EmVer): boolean { return this.greaterThan(other) || this.equals(other); } public lessThanOrEqual(other: EmVer): boolean { return !this.greaterThan(other); } public lessThan(other: EmVer): boolean { return !this.greaterThanOrEqual(other); } public compare(other: EmVer) { if (this.equals(other)) { return "equal" as const; } else if (this.greaterThan(other)) { return "greater" as const; } else { return "less" as const; } } public compareForSort(other: EmVer) { return matches.matches(this.compare(other)) .when("equal", () => 0 as const) .when("greater", () => 1 as const) .when("less", () => -1 as const) .unwrap(); }}
export class Checker { static parse(range: string | Checker): Checker { if (range instanceof Checker) { return range; } range = range.trim(); if (range.indexOf("||") !== -1) { return rangeOr(...range.split("||").map((x) => Checker.parse(x))); } if (range.indexOf("&&") !== -1) { return rangeAnd(...range.split("&&").map((x) => Checker.parse(x))); } if (range === "*") { return new Checker((version) => { EmVer.from(version); return true; }); } if (range.startsWith("!")) { return Checker.parse(range.substring(1)).not(); } const starSubMatches = starSub.exec(range); if (starSubMatches != null) { const emVarLower = EmVer.parse(starSubMatches[1]); const emVarUpper = emVarLower.withLastIncremented();
return new Checker((version) => { const v = EmVer.from(version); return (v.greaterThan(emVarLower) || v.equals(emVarLower)) && !v.greaterThan(emVarUpper) && !v.equals(emVarUpper); }); }
switch (range.substring(0, 2)) { case ">=": { const emVar = EmVer.parse(range.substring(2)); return new Checker((version) => { const v = EmVer.from(version); return v.greaterThanOrEqual(emVar); }); } case "<=": { const emVar = EmVer.parse(range.substring(2)); return new Checker((version) => { const v = EmVer.from(version); return v.lessThanOrEqual(emVar); }); } }
switch (range.substring(0, 1)) { case ">": { console.log("greaterThan"); const emVar = EmVer.parse(range.substring(1)); return new Checker((version) => { const v = EmVer.from(version); return v.greaterThan(emVar); }); } case "<": { const emVar = EmVer.parse(range.substring(1)); return new Checker((version) => { const v = EmVer.from(version); return v.lessThan(emVar); }); } case "=": { const emVar = EmVer.parse(range.substring(1)); return new Checker((version) => { const v = EmVer.from(version); return v.equals(emVar); }); } } throw new Error("Couldn't parse range: " + range); } constructor( public readonly check: (value: string | EmVer) => boolean, ) {}
public and(...others: (Checker | string)[]): Checker { return new Checker((value) => { if (!this.check(value)) { return false; } for (const other of others) { if (!Checker.parse(other).check(value)) { return false; } } return true; }); }
public or(...others: (Checker | string)[]): Checker { return new Checker((value) => { if (this.check(value)) { return true; } for (const other of others) { if (Checker.parse(other).check(value)) { return true; } } return false; }); }
public not(): Checker { return new Checker((value) => !this.check(value)); }}