From d93e3d0d305af13d60426f8c0003920e12ce21fc Mon Sep 17 00:00:00 2001 From: Sam Maselli Date: Tue, 14 Jan 2025 01:25:51 -0500 Subject: [PATCH] fix json parser --- .../gameModeSettings/OutlineSelector.tsx | 14 ++- .../gameModeSettings/gameMode/dataFixer/v4.ts | 102 +++++++++++++++--- client/src/game/roleListState.d.tsx | 8 +- 3 files changed, 100 insertions(+), 24 deletions(-) diff --git a/client/src/components/gameModeSettings/OutlineSelector.tsx b/client/src/components/gameModeSettings/OutlineSelector.tsx index c763f8baa..00c31abf0 100644 --- a/client/src/components/gameModeSettings/OutlineSelector.tsx +++ b/client/src/components/gameModeSettings/OutlineSelector.tsx @@ -28,7 +28,7 @@ export default function RoleOutlineSelector(props: RoleOutlineSelectorProps): Re {props.roleOutline.map((option, index) => { let roleOrRoleSet: RoleOrRoleSet; - if (option.role) { + if ("role" in option) { roleOrRoleSet = { type: "role", role: option.role @@ -71,12 +71,16 @@ export default function RoleOutlineSelector(props: RoleOutlineSelectorProps): Re let options = [...props.roleOutline]; switch (value.type) { case "role": - delete options[index].roleSet; - options[index].role = value.role; + options[index] = { + role: value.role, + ...options[index] + } break; case "roleSet": - options[index].roleSet = value.roleSet; - delete options[index].role; + options[index] = { + roleSet: value.roleSet, + ...options[index] + } break; } diff --git a/client/src/components/gameModeSettings/gameMode/dataFixer/v4.ts b/client/src/components/gameModeSettings/gameMode/dataFixer/v4.ts index bcf9d7456..b7ef08ba7 100644 --- a/client/src/components/gameModeSettings/gameMode/dataFixer/v4.ts +++ b/client/src/components/gameModeSettings/gameMode/dataFixer/v4.ts @@ -1,7 +1,8 @@ import { VersionConverter } from "."; import { GameMode, GameModeData, GameModeStorage, ShareableGameMode } from ".."; +import { Conclusion, CONCLUSIONS, INSIDER_GROUPS, InsiderGroup } from "../../../../game/gameState.d"; import { getDefaultSettings, Settings } from "../../../../game/localStorage"; -import { RoleOutline, RoleOutlineOption } from "../../../../game/roleListState.d"; +import { RoleOutline, RoleOutlineOption, RoleSet } from "../../../../game/roleListState.d"; import { Role } from "../../../../game/roleState.d"; import { Failure, ParseResult, ParseSuccess, Success, isFailure } from "../parse"; import { parseName, parsePhaseTimes, parseRole, parseRoleSet } from "./initial"; @@ -214,21 +215,94 @@ function parseRoleOutlineOptionList(json: NonNullable): ParseResult): ParseResult { - if (json.role !== undefined) { - const role = parseRole(json.role); - if (isFailure(role)) return role; - return Success({ - role: role.value - }); - } else if (json.roleSet !== undefined) { - const roleSet = parseRoleSet(json.roleSet); - if (isFailure(roleSet)) return roleSet; + let out: { + insiderGroups?: InsiderGroup[], + winIfAny?: Conclusion[], + role?: Role, + roleSet?: RoleSet + } = {} + + + if("insiderGroups" in json) { + const insiderGroupsResult = parseRoleOutlineOptionInsiderGroups(json.insiderGroups); + if (isFailure(insiderGroupsResult)) return insiderGroupsResult; + out.insiderGroups = insiderGroupsResult.value; + } + + if("winIfAny" in json) { + const winIfAnyResult = parseRoleOutlineOptionWinIfAny(json.winIfAny); + if (isFailure(winIfAnyResult)) return winIfAnyResult; + out.winIfAny = winIfAnyResult.value; + } - return Success ({ - roleSet: roleSet.value - }); + if("role" in json && "roleSet" in json) { + return Failure("roleOutlineOptionBothRoleAndRoleSet", json); + } + + if ("role" in json) { + const roleResult = parseRole(json.role); + if (isFailure(roleResult)) return roleResult; + out.role = roleResult.value; + } else if ("roleSet" in json) { + const roleSetResult = parseRoleSet(json.roleSet); + if (isFailure(roleSetResult)) return roleSetResult; + out.roleSet = roleSetResult.value; } else { - return Failure("roleOutlineOptionInvalidType", json); + return Failure("roleOutlineOptionNeitherRoleNorRoleSet", json); } + + return Success(out as RoleOutlineOption); +} + + +function parseRoleOutlineOptionWinIfAny(json: NonNullable): ParseResult { + if (!Array.isArray(json)) { + return Failure("winIfAnyNotArray", json); + } + + const conclusions = json.map(parseConclusion); + for (const conclusion of conclusions) { + if (isFailure(conclusion)) return conclusion; + } + + return Success(conclusions.map(success => (success as ParseSuccess).value)); +} + +function parseConclusion(json: NonNullable): ParseResult { + if (typeof json !== "string") { + return Failure("conclusionNotString", json); + } + + if (!CONCLUSIONS.includes(json as Conclusion)) { + return Failure("conclusionInvalid", json); + } + + return Success(json as Conclusion); +} + + +function parseRoleOutlineOptionInsiderGroups(json: NonNullable): ParseResult { + if (!Array.isArray(json)) { + return Failure("insiderGroupsNotArray", json); + } + + const insiderGroups = json.map(parseInsiderGroup); + for (const group of insiderGroups) { + if (isFailure(group)) return group; + } + + return Success(insiderGroups.map(success => (success as ParseSuccess).value)); +} + +function parseInsiderGroup(json: NonNullable): ParseResult { + if (typeof json !== "string") { + return Failure("insiderGroupNotString", json); + } + + if (!INSIDER_GROUPS.includes(json as InsiderGroup)) { + return Failure("insiderGroupInvalid", json); + } + + return Success(json as InsiderGroup); } \ No newline at end of file diff --git a/client/src/game/roleListState.d.tsx b/client/src/game/roleListState.d.tsx index 4ceba94a6..af5823f9b 100644 --- a/client/src/game/roleListState.d.tsx +++ b/client/src/game/roleListState.d.tsx @@ -48,9 +48,7 @@ export type RoleOutline = RoleOutlineOption[]; export type RoleOutlineOption = ({ roleSet: RoleSet - role?: undefined } | { - roleSet?: undefined role: Role }) & { winIfAny?: Conclusion[], @@ -84,9 +82,9 @@ export function translateRoleOutlineOption(roleOutlineOption: RoleOutlineOption) if (roleOutlineOption.winIfAny) { out += `${translateWinCondition({ type: "gameConclusionReached", winIfAny: roleOutlineOption.winIfAny })} ` } - if (roleOutlineOption.roleSet) { + if ("roleSet" in roleOutlineOption) { out += translate(roleOutlineOption.roleSet) - } else if (roleOutlineOption.role) { + } else { out += translate("role."+roleOutlineOption.role+".name") } return out; @@ -103,7 +101,7 @@ export function getRolesFromOutline(roleOutline: RoleOutline): Role[] { return roleOutline.flatMap((option) => getRolesFromOutlineOption(option)); } export function getRolesFromOutlineOption(roleOutlineOption: RoleOutlineOption): Role[] { - if (roleOutlineOption.roleSet) { + if ("roleSet" in roleOutlineOption) { return getRolesFromRoleSet(roleOutlineOption.roleSet) } else { return [roleOutlineOption.role]