mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-12-29 10:04:49 -05:00
Deprecated GameVersionResolver
It was replaced with `GameVersionFilter`
This commit is contained in:
parent
d50f67b65c
commit
40ffa003d6
4 changed files with 462 additions and 43 deletions
254
src/games/game-version-filter.ts
Normal file
254
src/games/game-version-filter.ts
Normal 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>;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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[]>;
|
||||
}
|
208
tests/unit/games/game-version-filter.spec.ts
Normal file
208
tests/unit/games/game-version-filter.spec.ts
Normal 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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue