ENUMS: Followup for #859 (#868)

This commit is contained in:
Snarling 2023-10-17 07:19:32 -04:00 committed by GitHub
parent 9c41995e59
commit 38f693e2c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 46 additions and 149 deletions

@ -13,10 +13,8 @@ export function determineCrimeSuccess(type: CrimeType): boolean {
}
export function findCrime(roughName: string): Crime | null {
const helper = getEnumHelper("CrimeType");
if (helper.isMember(roughName)) return Crimes[roughName];
const fuzzMatch = getEnumHelper("CrimeType").fuzzyGetMember(roughName);
if (fuzzMatch) return Crimes[fuzzMatch];
const matchedName = getEnumHelper("CrimeType").getMember(roughName, { fuzzy: true });
if (matchedName) return Crimes[matchedName];
// This can probably all be removed
roughName = roughName.toLowerCase();
if (roughName.includes("shoplift")) return Crimes[CrimeType.shoplift];

@ -32,7 +32,7 @@ import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
import { Companies } from "../Company/Companies";
import { Factions } from "../Faction/Factions";
import { helpers, assertString } from "../Netscript/NetscriptHelpers";
import { helpers } from "../Netscript/NetscriptHelpers";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { getServerOnNetwork } from "../Server/ServerHelpers";
import { Terminal } from "../Terminal";
@ -738,22 +738,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
applyToCompany: (ctx) => (_companyName, _field) => {
helpers.checkSingularityAccess(ctx);
const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName);
assertString(ctx, "field", _field);
// capitalize each word, except for "part-time"
function capitalizeJobField(field: string) {
return field
.toLowerCase()
.split(" ")
.map((s) => {
if (s.length == 0 || s == "part-time") return s;
if (s.length == 2) return s.toUpperCase(); // Probably an acronym
return s[0].toUpperCase() + s.slice(1);
})
.join(" ");
}
const field = getEnumHelper("JobField").nsGetMember(ctx, capitalizeJobField(_field as string), "field");
const field = getEnumHelper("JobField").nsGetMember(ctx, _field, "field", { fuzzy: true });
Player.location = companyNameAsLocationName(companyName);
let res;

@ -300,7 +300,7 @@ export class Sleeve extends Person implements SleevePerson {
};
if (workTypeConversion[_workType]) _workType = workTypeConversion[_workType];
const faction = Factions[factionName];
const workType = getEnumHelper("FactionWorkType").fuzzyGetMember(_workType);
const workType = getEnumHelper("FactionWorkType").getMember(_workType, { fuzzy: true });
if (!workType) return false;
const factionInfo = faction.getInfo();

@ -67,8 +67,10 @@ export class SleeveFactionWork extends SleeveWorkClass {
/** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveFactionWork {
const factionWork = Generic_fromJSON(SleeveFactionWork, value.data);
factionWork.factionWorkType = getEnumHelper("FactionWorkType").fuzzyGetMember(factionWork.factionWorkType, true);
factionWork.factionName = getEnumHelper("FactionName").fuzzyGetMember(factionWork.factionName, true);
factionWork.factionWorkType = getEnumHelper("FactionWorkType").getMember(factionWork.factionWorkType, {
alwaysMatch: true,
});
factionWork.factionName = getEnumHelper("FactionName").getMember(factionWork.factionName, { alwaysMatch: true });
return factionWork;
}
}

@ -271,7 +271,7 @@ function getABC(sleeve: Sleeve): [string, string, string] {
return ["Workout at Gym", gymNames[work.classType as GymType], work.location];
}
case SleeveWorkType.CRIME:
return ["Commit Crime", getEnumHelper("CrimeType").fuzzyGetMember(work.crimeType, true), "------"];
return ["Commit Crime", getEnumHelper("CrimeType").getMember(work.crimeType, { alwaysMatch: true }), "------"];
case SleeveWorkType.SUPPORT:
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];
case SleeveWorkType.INFILTRATE:

@ -6873,9 +6873,9 @@ declare enum JobField {
security = "Security",
agent = "Agent",
employee = "Employee",
partTimeEmployee = "part-time Employee",
partTimeEmployee = "Part-time Employee",
waiter = "Waiter",
partTimeWaiter = "part-time Waiter",
partTimeWaiter = "Part-time Waiter",
}
// CORP ENUMS - Changed to types

@ -98,7 +98,7 @@ export class CrimeWork extends Work {
/** Initializes a CrimeWork object from a JSON save state. */
static fromJSON(value: IReviverValue): CrimeWork {
const crimeWork = Generic_fromJSON(CrimeWork, value.data);
crimeWork.crimeType = getEnumHelper("CrimeType").fuzzyGetMember(crimeWork.crimeType, true);
crimeWork.crimeType = getEnumHelper("CrimeType").getMember(crimeWork.crimeType, { alwaysMatch: true });
return crimeWork;
}
}

@ -75,7 +75,7 @@ export enum JobField {
security = "Security",
agent = "Agent",
employee = "Employee",
partTimeEmployee = "part-time Employee",
partTimeEmployee = "Part-time Employee",
waiter = "Waiter",
partTimeWaiter = "part-time Waiter",
partTimeWaiter = "Part-time Waiter",
}

@ -91,8 +91,10 @@ export class FactionWork extends Work {
/** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): FactionWork {
const factionWork = Generic_fromJSON(FactionWork, value.data);
factionWork.factionWorkType = getEnumHelper("FactionWorkType").fuzzyGetMember(factionWork.factionWorkType, true);
factionWork.factionName = getEnumHelper("FactionName").fuzzyGetMember(factionWork.factionName, true);
factionWork.factionWorkType = getEnumHelper("FactionWorkType").getMember(factionWork.factionWorkType, {
alwaysMatch: true,
});
factionWork.factionName = getEnumHelper("FactionName").getMember(factionWork.factionName, { alwaysMatch: true });
return factionWork;
}
}

@ -5,6 +5,13 @@ import * as allEnums from "../Enums";
import { assertString, helpers } from "../Netscript/NetscriptHelpers";
import { getRandomInt } from "./helpers/getRandomInt";
interface GetMemberOptions {
/** Whether to use fuzzy matching on the input (case insensitive, ignore spaces and dashes) */
fuzzy?: boolean;
/** Whether to always return an enum member, even if there was no match. Will attempt fuzzy match before returning a default match. */
alwaysMatch?: boolean;
}
class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & string> {
name: string; // Name, for including in error text
defaultArgName: string; // Used as default for for validating ns arg name
@ -24,12 +31,19 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
return (this.valueSet.has as (value: unknown) => boolean)(toValidate);
}
/** Take an unknown input from a player script, either return an enum member or throw */
nsGetMember(ctx: NetscriptContext, toValidate: unknown, argName = this.defaultArgName): EnumMember {
if (this.isMember(toValidate)) return toValidate;
// assertString is just called so if the user didn't even pass in a string, they get a different error message
nsGetMember(
ctx: NetscriptContext,
toValidate: unknown,
argName = this.defaultArgName,
options?: GetMemberOptions,
): EnumMember {
const match = this.getMember(toValidate, options);
if (match) return match;
// No match found, create error message
assertString(ctx, argName, toValidate);
// Don't display all possibilities for large enums
let allowableValues = `Allowable values: ${this.valueArray.map((val) => `"${val}"`).join(", ")}`;
// Don't display all possibilities for large enums
if (this.valueArray.length > 10) {
console.warn(
`Provided value ${toValidate} was not a valid option for enum type ${this.name}.\n${allowableValues}`,
@ -41,19 +55,16 @@ class EnumHelper<EnumObj extends object, EnumMember extends Member<EnumObj> & st
`Argument ${argName} should be a ${this.name} enum member.\nProvided value: "${toValidate}".\n${allowableValues}`,
);
}
/** Provides case insensitivty and ignores spaces and dashes, and can always match the input */
fuzzyGetMember(input: string): EnumMember | undefined;
fuzzyGetMember(input: string, alwaysMatch: true): EnumMember;
fuzzyGetMember(input: string, alwaysMatch = false) {
const matchedValue = this.fuzzMap.get(input.toLowerCase().replace(/[ -]+/g, ""));
if (matchedValue) {
return matchedValue;
getMember(input: unknown, options: { alwaysMatch: true }): EnumMember;
getMember(input: unknown, options?: GetMemberOptions): EnumMember | undefined;
getMember(input: unknown, options?: GetMemberOptions): EnumMember | undefined {
if (this.isMember(input)) return input;
if (typeof input !== "string") return options?.alwaysMatch ? this.valueArray[0] : undefined;
if (options?.fuzzy || options?.alwaysMatch) {
const fuzzMatch = this.fuzzMap.get(input.toLowerCase().replace(/[ -]+/g, ""));
if (fuzzMatch) return fuzzMatch;
}
return alwaysMatch ? this.valueArray[0] : undefined;
}
/** Provide a case sensitive match, or undefined if */
getMember(input: unknown): EnumMember | undefined {
return this.isMember(input) ? input : undefined;
return undefined;
}
// Get a random enum member
random() {

@ -1,101 +0,0 @@
import { Player, setPlayer } from "../../../src/Player";
import { getCompaniesMetadata } from "../../../src/Company/data/CompaniesMetadata";
import { getCompanyPositionMetadata } from "../../../src/Company/data/CompanyPositionsMetadata";
import { PlayerObject } from "../../../src/PersonObjects/Player/PlayerObject";
import { NetscriptSingularity } from "../../../src/NetscriptFunctions/Singularity";
import { CompanyName, JobField, JobName } from "@enums";
import { WorkerScript } from "../../../src/Netscript/WorkerScript";
import { Script } from "../../../src/Script/Script";
import { RunningScript } from "../../../src/Script/RunningScript";
import { ScriptFilePath } from "../../../src/Paths/ScriptFilePath";
import { GetServer } from "../../../src/Server/AllServers";
describe("Singularity", () => {
let ctx;
let singularity;
let positionMetadata;
let companyMetadata;
beforeAll(() => {
singularity = NetscriptSingularity();
positionMetadata = getCompanyPositionMetadata();
companyMetadata = getCompaniesMetadata();
});
beforeEach(() => {
setPlayer(new PlayerObject());
Player.init();
Player.sourceFiles.set(4, 3);
GetServer("home").writeToScriptFile("function.js", "");
let script = new Script("function.js", "", "home");
let runningScript = new RunningScript(script, 1, []);
let workerScript = new WorkerScript(runningScript, 1);
ctx = { workerScript: workerScript, function: "singularityTest", functionPath: "test.singularityTest" };
});
afterEach(() => {
Object.values(CompanyName).forEach((k) => {
companyMetadata[k].playerReputation = 0;
});
});
describe("getCompanyPositionInfo", () => {
it("returns an enum for field", () => {
let companyWithPositions = Object.values(CompanyName).find(
(cn) => companyMetadata[cn].companyPositions.length > 0,
);
let company = companyMetadata[companyWithPositions];
let positionName = company.companyPositions[0];
let position = positionMetadata[positionName];
let companyPosition = singularity.getCompanyPositionInfo(ctx)(company.name, positionName);
expect(companyPosition.field).toEqual(position.field);
});
});
describe("applyToCompany", () => {
it("throws an error if input doesn't match an enum", () => {
let anyValidCompany = Object.values(CompanyName)[0];
expect(() => singularity.applyToCompany(ctx)(anyValidCompany, "sockware")).toThrow("should be a JobField");
expect(() => singularity.applyToCompany(ctx)(anyValidCompany, "invalid-job-field")).toThrow(
"should be a JobField",
);
});
it("accepts the JobField specified by getCompanyPositionInfo", () => {
Object.values(CompanyName).forEach((cn) => {
companyMetadata[cn].companyPositions.forEach((pn) => {
let pos = positionMetadata[pn];
expect(() => singularity.applyToCompany(ctx)(cn, pos.field)).not.toThrow();
Player.quitJob(cn);
});
});
});
it("is case-insensitive to string inputs to the field parameter", () => {
Object.values(CompanyName).forEach((cn) => {
companyMetadata[cn].companyPositions.forEach((pn) => {
let pos = positionMetadata[pn];
let field = pos.field;
let upperCase = field.toUpperCase();
expect(() => singularity.applyToCompany(ctx)(cn, upperCase)).not.toThrow();
Player.quitJob(cn);
let lowerCase = field.toLowerCase();
expect(() => singularity.applyToCompany(ctx)(cn, lowerCase)).not.toThrow();
Player.quitJob(cn);
let brokenCasing = field
.split("")
.map((c, i) => (i % 2 == 0 ? c.toUpperCase() : c.toLowerCase()))
.join("");
expect(() => singularity.applyToCompany(ctx)(cn, brokenCasing)).not.toThrow();
Player.quitJob(cn);
});
});
});
});
});