Deprecated GameVersionResolver

It was replaced with `GameVersionFilter`
This commit is contained in:
Kir_Antipov 2023-03-25 13:54:21 +00:00
parent d50f67b65c
commit 40ffa003d6
4 changed files with 462 additions and 43 deletions

View file

@ -0,0 +1,254 @@
import { Enum, EnumOptions } from "@/utils/enum";
import { stringEquals } from "@/utils/string-utils";
import { deprecate } from "node:util";
import { GameVersion } from "./game-version";
// TODO: Remove deprecated stuff in v4.0
/**
* Represents a game version filter.
*
* This filter can be used to filter game versions based on the provided criteria.
*
* @partial
*/
enum GameVersionFilterValues {
/**
* No filter applied.
*/
NONE = 0,
/**
* Filter to include release versions.
*/
RELEASES = 1,
/**
* Filter to include beta versions.
*/
BETAS = 2,
/**
* Filter to include alpha versions.
*/
ALPHAS = 4,
/**
* Filter to include both alpha and beta versions (snapshots).
*/
SNAPSHOTS = ALPHAS | BETAS,
/**
* Filter to include any version type.
*/
ANY = RELEASES | SNAPSHOTS,
/**
* Filter to include versions with the minimum patch number.
*/
MIN_PATCH = 8,
/**
* Filter to include versions with the maximum patch number.
*/
MAX_PATCH = 16,
/**
* Filter to include versions with the minimum minor number.
*/
MIN_MINOR = 32,
/**
* Filter to include versions with the maximum minor number.
*/
MAX_MINOR = 64,
/**
* Filter to include versions with the minimum major number.
*/
MIN_MAJOR = 128,
/**
* Filter to include versions with the maximum major number.
*/
MAX_MAJOR = 256,
/**
* Filter to include the last version in a range, considering major, minor, and patch numbers.
*/
MIN = MIN_MAJOR | MIN_MINOR | MIN_PATCH,
/**
* Filter to include the first version in a range, considering major, minor, and patch numbers.
*/
MAX = MAX_MAJOR | MAX_MINOR | MAX_PATCH,
}
/**
* Options for configuring the behavior of the `GameVersionFilter` enum.
*
* @partial
*/
const GameVersionFilterOptions: EnumOptions = {
/**
* `GameVersionFilter` is a flag-based enum.
*/
hasFlags: true,
/**
* The case should be ignored while parsing the filter.
*/
ignoreCase: true,
/**
* Non-word characters should be ignored while parsing the filter.
*/
ignoreNonWordCharacters: true,
};
/**
* Filters game versions based on the provided filter.
*
* @template T - The type of the game versions.
*
* @param versions - An iterable of game versions to filter.
* @param filter - The filter to apply to the versions.
*
* @returns An array of filtered game versions.
*/
function filter<T extends GameVersion>(versions: Iterable<T>, filter: GameVersionFilter): T[] {
let filtered = [...versions];
if (filter === GameVersionFilter.NONE || !filter) {
return filtered;
}
filtered = filterVersionType(filtered, filter);
filtered = applyVersionRange(filtered, x => x.version.major, filter, GameVersionFilter.MIN_MAJOR, GameVersionFilter.MAX_MAJOR);
filtered = applyVersionRange(filtered, x => x.version.minor, filter, GameVersionFilter.MIN_MINOR, GameVersionFilter.MAX_MINOR);
filtered = applyVersionRange(filtered, x => x.version.patch, filter, GameVersionFilter.MIN_PATCH, GameVersionFilter.MAX_PATCH);
return filtered;
}
/**
* Filters game versions based on version type.
*
* @template T - The type of the game versions.
*
* @param versions - An array of game versions to filter.
* @param filter - The filter to apply to the versions.
*
* @returns An array of filtered game versions.
*/
function filterVersionType<T extends GameVersion>(versions: T[], filter: GameVersionFilter): T[] {
const allowReleases = GameVersionFilter.hasFlag(filter, GameVersionFilter.RELEASES);
const allowBetas = GameVersionFilter.hasFlag(filter, GameVersionFilter.BETAS);
const allowAlphas = GameVersionFilter.hasFlag(filter, GameVersionFilter.ALPHAS);
const allowAny = (allowReleases && allowBetas && allowAlphas) || !(allowReleases || allowBetas || allowAlphas);
if (!allowAny) {
return versions.filter(x => (!x.isRelease || allowReleases) && (!x.isBeta || allowBetas) && (!x.isAlpha || allowAlphas));
}
return versions;
}
/**
* Applies a version range filter based on the provided flags.
*
* @template T - The type of the game versions.
*
* @param versions - An array of game versions to filter.
* @param selector - A function to select a specific version value (major, minor, or patch).
* @param flags - The filter flags to apply to the versions.
* @param minFlag - The `minimum` flag applicable to the selected version value.
* @param maxFlag - The `maximum` flag applicable to the selected version value.
*
* @returns An array of filtered game versions.
*/
function applyVersionRange<T extends GameVersion>(versions: T[], selector: (x: T) => number, flags: number, minFlag: number, maxFlag: number): T[] {
const comparer = GameVersionFilter.hasFlag(flags, minFlag) ? -1 : GameVersionFilter.hasFlag(flags, maxFlag) ? 1 : 0;
if (!comparer) {
return versions;
}
const target = versions.reduce((current, version) => Math.sign(selector(version) - current) === comparer ? selector(version) : current, comparer === 1 ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER);
return versions.filter(x => selector(x) === target);
}
/**
* Converts a version resolver name to a game version filter.
*
* @param versionResolverName - The name of the version resolver.
*
* @returns The corresponding game version filter.
*/
function _fromVersionResolver(versionResolverName: string): GameVersionFilter {
if (stringEquals(versionResolverName, "exact", { ignoreCase: true })) {
return GameVersionFilterValues.MIN | GameVersionFilterValues.RELEASES;
}
if (stringEquals(versionResolverName, "latest", { ignoreCase: true })) {
return (
GameVersionFilterValues.MIN_MAJOR |
GameVersionFilterValues.MIN_MINOR |
GameVersionFilterValues.MAX_PATCH |
GameVersionFilterValues.RELEASES
);
}
if (stringEquals(versionResolverName, "all", { ignoreCase: true })) {
return GameVersionFilterValues.MIN_MAJOR | GameVersionFilterValues.MIN_MINOR;
}
return (
GameVersionFilterValues.MIN_MAJOR |
GameVersionFilterValues.MIN_MINOR |
GameVersionFilterValues.RELEASES
);
}
/**
* Converts a version resolver name to a game version filter.
*
* @param versionResolverName - The name of the version resolver.
*
* @returns The corresponding game version filter.
*
* @deprecated
*
* Use keys of the new {@link GameVersionFilter} instead.
*/
const fromVersionResolver = deprecate(
_fromVersionResolver,
"Use the new `game-version-filter` input instead of the deprecated `version-resolver` one."
);
/**
* A collection of methods to work with `GameVersionFilter`.
*
* @partial
*/
const GameVersionFilterMethods = {
filter,
fromVersionResolver,
};
/**
* Represents a game version filter.
*
* This filter can be used to filter game versions based on the provided criteria.
*/
export const GameVersionFilter = Enum.create(
GameVersionFilterValues,
GameVersionFilterOptions,
GameVersionFilterMethods,
);
/**
* Represents a game version filter.
*
* This filter can be used to filter game versions based on the provided criteria.
*/
export type GameVersionFilter = Enum<typeof GameVersionFilterValues>;

View file

@ -1,24 +0,0 @@
import GameVersionResolver from "../versioning/game-version-resolver";
import { getCompatibleBuilds, MinecraftVersion } from ".";
import Version from "../versioning/version";
export default class MinecraftVersionResolver extends GameVersionResolver<MinecraftVersion> {
public static readonly exact = new MinecraftVersionResolver((n, v) => [v.find(x => x.version.equals(n))].filter(x => x));
public static readonly latest = new MinecraftVersionResolver((_, v) => v.find(x => x.isRelease) ? [v.find(x => x.isRelease)] : v.length ? [v[0]] : []);
public static readonly all = new MinecraftVersionResolver((_, v) => v);
public static readonly releases = new MinecraftVersionResolver((_, v) => v.filter(x => x.isRelease));
public static readonly releasesIfAny = new MinecraftVersionResolver((_, v) => v.find(x => x.isRelease) ? v.filter(x => x.isRelease) : v);
public static byName(name: string): MinecraftVersionResolver | null {
for (const [key, value] of Object.entries(MinecraftVersionResolver)) {
if (value instanceof MinecraftVersionResolver && key.localeCompare(name, undefined, { sensitivity: "accent" }) === 0) {
return value;
}
}
return null;
}
public getCompatibleVersions(version: string | Version): Promise<MinecraftVersion[]> {
return getCompatibleBuilds(version);
}
}

View file

@ -1,19 +0,0 @@
import Version from "./version";
export default abstract class GameVersionResolver<TGameVersion> {
private readonly _filter: (version: string | Version, versions: TGameVersion[]) => TGameVersion[];
protected constructor(filter?: (version: string | Version, versions: TGameVersion[]) => TGameVersion[]) {
this._filter = filter || ((_, x) => x);
}
public async resolve(version: string | Version): Promise<TGameVersion[]> {
return this.filter(version, await this.getCompatibleVersions(version));
}
public filter(version: string | Version, versions: TGameVersion[]): TGameVersion[] {
return this._filter(version, versions);
}
public abstract getCompatibleVersions(version: string | Version): Promise<TGameVersion[]>;
}

View file

@ -0,0 +1,208 @@
import { parseVersion } from "@/utils/versioning/version";
import { GameVersion } from "@/games/game-version";
import { GameVersionFilter } from "@/games/game-version-filter";
describe("GameVersionFilter", () => {
describe("filter", () => {
let GAME_VERSIONS = undefined as GameVersion[];
beforeEach(() => {
GAME_VERSIONS = [
{ id: "1.0.0-alpha.1", version: parseVersion("1.0.0-alpha.1"), isRelease: false, isSnapshot: true, isAlpha: true, isBeta: false },
{ id: "1.0.0", version: parseVersion("1.0.0"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
{ id: "1.1.0-beta.2", version: parseVersion("1.1.0-beta.2"), isRelease: false, isSnapshot: true, isAlpha: false, isBeta: true },
{ id: "1.1.0", version: parseVersion("1.1.0"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
{ id: "1.2.0-beta.2", version: parseVersion("1.2.0-beta.2"), isRelease: false, isSnapshot: true, isAlpha: false, isBeta: true },
{ id: "1.2.0", version: parseVersion("1.2.0"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
{ id: "1.2.1", version: parseVersion("1.2.1"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
{ id: "1.2.2", version: parseVersion("1.2.2"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
{ id: "1.2.3", version: parseVersion("1.2.3"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
{ id: "2.0.0", version: parseVersion("2.0.0"), isRelease: true, isSnapshot: false, isAlpha: false, isBeta: false },
];
});
describe("NONE", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.NONE)).not.toBe(GAME_VERSIONS);
});
test("an unfiltered array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.NONE)).toEqual(GAME_VERSIONS);
});
});
describe("RELEASES", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.RELEASES)).not.toBe(GAME_VERSIONS);
});
test("only releases are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.RELEASES);
expect(versions).toHaveLength(7);
expect(versions.every(x => x.isRelease)).toBe(true);
});
});
describe("ALPHAS", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.ALPHAS)).not.toBe(GAME_VERSIONS);
});
test("only alphas are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.ALPHAS);
expect(versions).toHaveLength(1);
expect(versions.every(x => x.isAlpha)).toBe(true);
});
});
describe("BETAS", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.BETAS)).not.toBe(GAME_VERSIONS);
});
test("only betas are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.BETAS);
expect(versions).toHaveLength(2);
expect(versions.every(x => x.isBeta)).toBe(true);
});
});
describe("SNAPSHOTS", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.SNAPSHOTS)).not.toBe(GAME_VERSIONS);
});
test("only snapshots are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.SNAPSHOTS);
expect(versions).toHaveLength(3);
expect(versions.every(x => x.isSnapshot)).toBe(true);
});
});
describe("MIN_PATCH", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MIN_PATCH)).not.toBe(GAME_VERSIONS);
});
test("only versions with the lowest patch value are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MIN_PATCH);
expect(versions).toHaveLength(7);
expect(versions.every(x => x.version.patch === 0)).toBe(true);
});
});
describe("MAX_PATCH", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MAX_PATCH)).not.toBe(GAME_VERSIONS);
});
test("only versions with the highest patch value are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MAX_PATCH);
expect(versions).toHaveLength(1);
expect(versions.every(x => x.version.patch === 3)).toBe(true);
});
});
describe("MIN_MINOR", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MIN_MINOR)).not.toBe(GAME_VERSIONS);
});
test("only versions with the lowest minor value are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MIN_MINOR);
expect(versions).toHaveLength(3);
expect(versions.every(x => x.version.minor === 0)).toBe(true);
});
});
describe("MAX_MINOR", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MAX_MINOR)).not.toBe(GAME_VERSIONS);
});
test("only versions with the highest minor value are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MAX_MINOR);
expect(versions).toHaveLength(5);
expect(versions.every(x => x.version.minor === 2)).toBe(true);
});
});
describe("MIN_MAJOR", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MIN_MAJOR)).not.toBe(GAME_VERSIONS);
});
test("only versions with the lowest major value are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MIN_MAJOR);
expect(versions).toHaveLength(9);
expect(versions.every(x => x.version.major === 1)).toBe(true);
});
});
describe("MAX_MAJOR", () => {
test("a different array is returned", () => {
expect(GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MAX_MAJOR)).not.toBe(GAME_VERSIONS);
});
test("only versions with the highest major value are returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.MAX_MAJOR);
expect(versions).toHaveLength(1);
expect(versions.every(x => x.version.major === 2)).toBe(true);
});
});
describe("RELEASES | MIN", () => {
test("the oldest version is returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.RELEASES | GameVersionFilter.MIN);
expect(versions).toHaveLength(1);
expect(versions[0]).toMatchObject({ id: "1.0.0" });
});
});
describe("RELEASES | MAX", () => {
test("the latest version is returned", () => {
const versions = GameVersionFilter.filter(GAME_VERSIONS, GameVersionFilter.RELEASES | GameVersionFilter.MAX);
expect(versions).toHaveLength(1);
expect(versions[0]).toMatchObject({ id: "2.0.0" });
});
});
});
describe("parse", () => {
test("parses all its own formatted values", () => {
for (const value of GameVersionFilter.values()) {
expect(GameVersionFilter.parse(GameVersionFilter.format(value))).toBe(value);
}
});
test("parses all friendly names of its own values", () => {
for (const value of GameVersionFilter.values()) {
expect(GameVersionFilter.parse(GameVersionFilter.friendlyNameOf(value))).toBe(value);
}
});
test("parses all its own formatted values in lowercase", () => {
for (const value of GameVersionFilter.values()) {
expect(GameVersionFilter.parse(GameVersionFilter.format(value).toLowerCase())).toBe(value);
}
});
test("parses all its own formatted values in UPPERCASE", () => {
for (const value of GameVersionFilter.values()) {
expect(GameVersionFilter.parse(GameVersionFilter.format(value).toUpperCase())).toBe(value);
}
});
});
});