From a6d307bf6b53d566cc583f69a4220d3e7d8b2f8b Mon Sep 17 00:00:00 2001 From: Martin Hula Date: Mon, 19 Aug 2019 20:17:39 +0200 Subject: [PATCH] First working version --- src/Bulletin.ts | 1 + src/Inspector.ts | 6 ++ src/Papers.ts | 8 +++ src/RulesetBuilder.ts | 62 ++++++++++++------- .../RequireVaccinationForNationExpression.ts | 3 + .../DiplomaticAuthorizationInterpreter.ts | 2 + src/interpreters/paper/WorkPassInterpreter.ts | 17 +++++ .../paper/expressions/AccessExpression.ts | 3 +- src/papers/DiplomaticAuthorization.ts | 8 +-- src/papers/WorkPass.ts | 3 + src/types.ts | 4 +- src/validators/DisjunctiveValidator.ts | 9 +++ .../HasCertificateOfVaccinationValidator.ts | 7 +++ src/validators/HasWorkPassValidator.ts | 7 +++ src/validators/IdConsistencyValidator.ts | 3 +- src/validators/IsCitizenOfNationValidator.ts | 3 +- .../IsCitizenOfUnknownNationValidator.ts | 8 +++ ...IsValidDiplomaticAuthorizationValidator.ts | 9 +++ 18 files changed, 132 insertions(+), 31 deletions(-) create mode 100644 src/interpreters/paper/WorkPassInterpreter.ts create mode 100644 src/papers/WorkPass.ts create mode 100644 src/validators/DisjunctiveValidator.ts create mode 100644 src/validators/HasCertificateOfVaccinationValidator.ts create mode 100644 src/validators/HasWorkPassValidator.ts create mode 100644 src/validators/IsCitizenOfUnknownNationValidator.ts create mode 100644 src/validators/IsValidDiplomaticAuthorizationValidator.ts diff --git a/src/Bulletin.ts b/src/Bulletin.ts index 48358f2..f92a2b3 100644 --- a/src/Bulletin.ts +++ b/src/Bulletin.ts @@ -58,6 +58,7 @@ export class Bulletin { this.requiredVaccinationsByNation[nation].add(vaccine); } public noLongerRequireVaccinationForNation (nation: Nation, vaccine: Vaccine): void { + console.log('noLongerRequireVaccinationForNation', nation, vaccine, this.requiredVaccinationsByNation[nation]); this.requiredVaccinationsByNation[nation] = this.requiredVaccinationsByNation[nation] || new Set(); this.requiredVaccinationsByNation[nation].delete(vaccine); } diff --git a/src/Inspector.ts b/src/Inspector.ts index 18ea7f2..0e66c7e 100644 --- a/src/Inspector.ts +++ b/src/Inspector.ts @@ -10,6 +10,7 @@ import { RulesetBuilder } from './RulesetBuilder'; import { Ruleset } from './Ruleset'; import { CertificateOfVaccinationInterpreter } from './interpreters/paper/CertificateOfVaccinationInterpreter'; import { IdCardInterpreter } from './interpreters/paper/IdCardInterpreter'; +import { WorkPassInterpreter } from './interpreters/paper/WorkPassInterpreter'; export class Inspector { private bulletin: Bulletin; @@ -82,6 +83,11 @@ export class Inspector { const idCard = interpreter.interpret(inputPapers.ID_card); papers.setIdCard(idCard); } + if (inputPapers.work_pass) { + const interpreter = new WorkPassInterpreter(); + const workPass = interpreter.interpret(inputPapers.work_pass); + papers.setWorkPass(workPass); + } return papers; } } \ No newline at end of file diff --git a/src/Papers.ts b/src/Papers.ts index 90c997e..5b8d3db 100644 --- a/src/Papers.ts +++ b/src/Papers.ts @@ -5,6 +5,7 @@ import { DiplomaticAuthorization } from './papers/DiplomaticAuthorization'; import { PersonalData } from './papers/PersonalData'; import { IdCard } from './papers/IdCard'; import { CertificateOfVaccination } from './papers/CertificateOfVaccination'; +import { WorkPass } from './papers/WorkPass'; export class Papers { private passport: Passport; @@ -13,6 +14,7 @@ export class Papers { private diplomaticAuthorization: DiplomaticAuthorization; private certificateOfVaccination: CertificateOfVaccination; private idCard: IdCard; + private workPass: WorkPass; private personalData: PersonalData; constructor () { @@ -43,6 +45,9 @@ export class Papers { this.idCard = idCard; this.personalData.fromIdCard(idCard); } + public setWorkPass (workPass: WorkPass): void { + this.workPass = workPass; + } public getPassport (): Passport { return this.passport; @@ -62,6 +67,9 @@ export class Papers { public getIdCard (): IdCard { return this.idCard; } + public getWorkPass (): WorkPass { + return this.workPass; + } public getPersonalData (): PersonalData { return this.personalData; } diff --git a/src/RulesetBuilder.ts b/src/RulesetBuilder.ts index 06eebf8..adc4923 100644 --- a/src/RulesetBuilder.ts +++ b/src/RulesetBuilder.ts @@ -23,9 +23,17 @@ import { NegateValidator } from './validators/NegateValidator'; import { NationConsistencyValidator } from './validators/NationConsistencyValidator'; import { HasIdCardValidator } from './validators/HasIDCardValidator'; import { HasVaccinationValidator } from './validators/HasVaccinationValidator'; +import { Arstotzka } from './constants'; +import { DisjunctiveValidator } from './validators/DisjunctiveValidator'; +import { HasWorkPassValidator } from './validators/HasWorkPassValidator'; +import { HasCertificateOfVaccinationValidator } from './validators/HasCertificateOfVaccinationValidator'; +import { IsValidDiplomaticAuthorizationValidator } from './validators/IsValidDiplomaticAuthorizationValidator'; +import { IsCitizenOfUnknownNationValidator } from './validators/IsCitizenOfUnknownNationValidator'; export class RulesetBuilder { - private deny: Rule[] = []; + private deny: Rule[] = [ + new Rule(new IsCitizenOfUnknownNationValidator(), 'missing required passport.') + ]; private detain: Rule[] = [ new Rule(new NegateValidator(new IdConsistencyValidator()), 'ID number mismatch.'), new Rule(new NegateValidator(new DOBConsistencyValidator()), 'Date of birth mismatch.'), @@ -36,9 +44,10 @@ export class RulesetBuilder { ]; public fromBulletin (bulletin: Bulletin): void { - bulletin.getDenied().forEach((nation: Nation) => { - this.deny.push(new Rule(new IsCitizenOfNationValidator(nation), 'citizen of banned nation.')); - }); + const wantedName = bulletin.getWantedName(); + if (wantedName) { + this.detain.unshift(new Rule(new IsAWantedCriminalValidator(wantedName), 'Entrant is a wanted criminal.')); + } const requiredDocumentsByNation = bulletin.getRequiredDocumentsByNation(); for (var nation in requiredDocumentsByNation) { if (requiredDocumentsByNation.hasOwnProperty(nation)) { @@ -47,6 +56,9 @@ export class RulesetBuilder { }); } } + bulletin.getDenied().forEach((nation: Nation) => { + this.deny.push(new Rule(new IsCitizenOfNationValidator(nation), 'citizen of banned nation.')); + }); const requiredDocumentsForWorkers = bulletin.getrequiredDocumentsForWorkers(); requiredDocumentsForWorkers.forEach((document: Document) => { this.addDocumentValidatorsForWorkers(document); @@ -55,14 +67,11 @@ export class RulesetBuilder { for (var nation in requiredVaccinationsByNation) { if (requiredVaccinationsByNation.hasOwnProperty(nation)) { requiredVaccinationsByNation[nation].forEach((vaccine: Vaccine) => { - this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasVaccinationValidator(vaccine))), 'missing required certificate of vaccination.')); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasCertificateOfVaccinationValidator())), 'missing required certificate of vaccination.')); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasVaccinationValidator(vaccine))), 'missing required vaccination.')); }); } } - const wantedName = bulletin.getWantedName(); - if (wantedName) { - this.detain.unshift(new Rule(new IsAWantedCriminalValidator(wantedName), 'Entrant is a wanted criminal.')); - } } private addDocumentValidatorsForNation (nation: Nation, document: Document) { @@ -75,17 +84,26 @@ export class RulesetBuilder { this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasIdCardValidator())), 'missing required ' + document + '.')); break; case 'access permit': - this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasAccessPermitValidator())), 'missing required ' + document + '.')); - this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new NegateValidator(new IsAccessPermitExpiredValidator()))), document + ' expired.')); - //if foreigner -> - //OR - //grant of asylum - valid - //OR - //diplomatic authorization - valid & Arstotzka in list of nations + if (nation === Arstotzka) { + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasAccessPermitValidator())), 'missing required ' + document + '.')); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new NegateValidator(new IsAccessPermitExpiredValidator()))), document + ' expired.')); + } else { + const hasAccessPermit = new HasAccessPermitValidator(); + const hasGrantOfAsylumOrDiplomaticAuthorization = new DisjunctiveValidator(new HasGrantOfAsylumValidator(), new HasDiplomaticAuthorizationValidator()); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new DisjunctiveValidator(hasAccessPermit, hasGrantOfAsylumOrDiplomaticAuthorization))), 'missing required ' + document + '.')); + const ifHasAccessPermitItIsValid = new ImplicativeValidator(hasAccessPermit, new NegateValidator(new IsAccessPermitExpiredValidator())); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), ifHasAccessPermitItIsValid)), document + ' expired.')); + const ifHasGrantOfAsylumItIsNotExpired = new ImplicativeValidator(new HasGrantOfAsylumValidator(), new NegateValidator(new IsGrantOfAsylumExpiredValidator())); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), ifHasGrantOfAsylumItIsNotExpired)), 'grant of asylum expired.')); + const ifHasDiplomaticAuthorizationItIsNotExpired = new ImplicativeValidator(new HasDiplomaticAuthorizationValidator(), new NegateValidator(new IsDiplomaticAuthorizationExpiredValidator())); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), ifHasDiplomaticAuthorizationItIsNotExpired)), 'diplomatic authorization expired.')); + const ifHasDiplomaticAuthorizationItIsValid = new ImplicativeValidator(new HasDiplomaticAuthorizationValidator(), new IsValidDiplomaticAuthorizationValidator()); + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), ifHasDiplomaticAuthorizationItIsValid)), 'invalid diplomatic authorization.')); + } + break; + case 'work pass': + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasWorkPassValidator())), 'missing required ' + document + '.')); break; - /*case 'work pass': - documentValidator = new HasWorkPassValidator(); - break;*/ case 'grant of asylum': this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new HasGrantOfAsylumValidator())), 'missing required ' + document + '.')); this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsCitizenOfNationValidator(nation), new NegateValidator(new IsGrantOfAsylumExpiredValidator()))), document + ' expired.')); @@ -115,9 +133,9 @@ export class RulesetBuilder { //OR //diplomatic authorization - valid & Arstotzka in list of nations break; - /*case 'work pass': - documentValidator = new HasWorkPassValidator(); - break;*/ + case 'work pass': + this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsWorkerValidator(), new HasWorkPassValidator())), 'missing required ' + document + '.')); + break; case 'grant of asylum': this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsWorkerValidator(), new HasGrantOfAsylumValidator())), 'missing required ' + document + '.')); this.deny.push(new Rule(new NegateValidator(new ImplicativeValidator(new IsWorkerValidator(), new NegateValidator(new IsGrantOfAsylumExpiredValidator()))), document + ' expired.')); diff --git a/src/interpreters/bulletin/expressions/RequireVaccinationForNationExpression.ts b/src/interpreters/bulletin/expressions/RequireVaccinationForNationExpression.ts index aae8477..0704226 100644 --- a/src/interpreters/bulletin/expressions/RequireVaccinationForNationExpression.ts +++ b/src/interpreters/bulletin/expressions/RequireVaccinationForNationExpression.ts @@ -4,6 +4,9 @@ import { Nation, Vaccine } from '../../../types'; export class RequireVaccinationForNationExpression extends Expression { protected processLine (line: string, bulletin: Bulletin): void { + const isNoLongerRule = line.includes('no longer'); + if (isNoLongerRule) return; + const requireRegex = /^Citizens of (.*) require (.*) vaccination$/; const lineDecomposition = line.trim().match(requireRegex); if (!lineDecomposition) return; diff --git a/src/interpreters/paper/DiplomaticAuthorizationInterpreter.ts b/src/interpreters/paper/DiplomaticAuthorizationInterpreter.ts index 34a0109..bd31e41 100644 --- a/src/interpreters/paper/DiplomaticAuthorizationInterpreter.ts +++ b/src/interpreters/paper/DiplomaticAuthorizationInterpreter.ts @@ -4,6 +4,7 @@ import { NationExpression } from './expressions/NationExpression'; import { ExpiryExpression } from './expressions/ExpiryExpression'; import { IdExpression } from './expressions/IdExpression'; import { DiplomaticAuthorization } from '../../papers/DiplomaticAuthorization'; +import { AccessExpression } from './expressions/AccessExpression'; export class DiplomaticAuthorizationInterpreter { @@ -11,6 +12,7 @@ export class DiplomaticAuthorizationInterpreter { const diplomaticAuthorization = new DiplomaticAuthorization(); const tree = []> []; tree.push(new IdExpression()); + tree.push(new AccessExpression()); tree.push(new NameExpression()); tree.push(new NationExpression()); tree.push(new ExpiryExpression()); diff --git a/src/interpreters/paper/WorkPassInterpreter.ts b/src/interpreters/paper/WorkPassInterpreter.ts new file mode 100644 index 0000000..4d154c4 --- /dev/null +++ b/src/interpreters/paper/WorkPassInterpreter.ts @@ -0,0 +1,17 @@ +import { Expression } from './expressions/Expression'; +import { AccessPermit } from '../../papers/AccessPermit'; +import { WorkPass } from '../../papers/WorkPass'; + +export class WorkPassInterpreter { + + public interpret (input: string): WorkPass { + const accessPermit = new AccessPermit(); + const tree = []> []; + + tree.forEach((expression: Expression) => { + expression.interpret(input, accessPermit); + }); + + return accessPermit; + } +} \ No newline at end of file diff --git a/src/interpreters/paper/expressions/AccessExpression.ts b/src/interpreters/paper/expressions/AccessExpression.ts index 57c6eeb..a75e186 100644 --- a/src/interpreters/paper/expressions/AccessExpression.ts +++ b/src/interpreters/paper/expressions/AccessExpression.ts @@ -6,6 +6,7 @@ export class AccessExpression extends Expression { return name === 'ACCESS'; } protected setValue (setter: AccessSetter, value: Nation): void { - setter.setAccess(value); + const nations = value.split(', '); + setter.setAccess(nations); } } \ No newline at end of file diff --git a/src/papers/DiplomaticAuthorization.ts b/src/papers/DiplomaticAuthorization.ts index ceeb78b..3dd2de4 100644 --- a/src/papers/DiplomaticAuthorization.ts +++ b/src/papers/DiplomaticAuthorization.ts @@ -2,13 +2,13 @@ import { Paper } from './Paper'; import { Nation } from '../types'; export class DiplomaticAuthorization extends Paper { - private access: Nation; + private access: Nation[]; - public setAccess (access: Nation): void { + public setAccess (access: Nation[]): void { this.access = access; } - public getAccess (): Nation { - return this.access; + public canAccess (nation: Nation): boolean { + return this.access.includes(nation); } } \ No newline at end of file diff --git a/src/papers/WorkPass.ts b/src/papers/WorkPass.ts new file mode 100644 index 0000000..3b80928 --- /dev/null +++ b/src/papers/WorkPass.ts @@ -0,0 +1,3 @@ +export class WorkPass { + +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 4e1139b..81b5d73 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,7 @@ import { DiplomaticAuthorization } from './papers/DiplomaticAuthorization'; import { PersonalData } from './papers/PersonalData'; import { IdCard } from './papers/IdCard'; import { CertificateOfVaccination } from './papers/CertificateOfVaccination'; +import { WorkPass } from './papers/WorkPass'; export type Nation = 'Arstotzka' | 'Antegria' | 'Impor' | 'Kolechia' | 'Obristan' | 'Republia' | 'United Federation' export type Sex = 'M' | 'F'; @@ -27,7 +28,7 @@ export interface InputPapers { } export interface AccessSetter { - setAccess (access: Nation): void; + setAccess (access: Nation[]): void; } export interface AccessGetter { getAccess (): Nation; @@ -112,6 +113,7 @@ export interface Papers { getDiplomaticAuthorization (): DiplomaticAuthorization; getCertificateOfVaccination (): CertificateOfVaccination; getIdCard (): IdCard; + getWorkPass (): WorkPass; getPersonalData (): PersonalData; } diff --git a/src/validators/DisjunctiveValidator.ts b/src/validators/DisjunctiveValidator.ts new file mode 100644 index 0000000..d07a127 --- /dev/null +++ b/src/validators/DisjunctiveValidator.ts @@ -0,0 +1,9 @@ +import { Validator, Papers } from '../types'; + +export class DisjunctiveValidator implements Validator { + constructor (private validatorA: Validator, private validatorB: Validator) {} + + public validate (papers: Papers): boolean { + return this.validatorA.validate(papers) || this.validatorB.validate(papers); + } +} diff --git a/src/validators/HasCertificateOfVaccinationValidator.ts b/src/validators/HasCertificateOfVaccinationValidator.ts new file mode 100644 index 0000000..23d08e5 --- /dev/null +++ b/src/validators/HasCertificateOfVaccinationValidator.ts @@ -0,0 +1,7 @@ +import { Validator, Papers } from '../types'; + +export class HasCertificateOfVaccinationValidator implements Validator { + public validate (papers: Papers): boolean { + return !!papers.getCertificateOfVaccination(); + } +} \ No newline at end of file diff --git a/src/validators/HasWorkPassValidator.ts b/src/validators/HasWorkPassValidator.ts new file mode 100644 index 0000000..0a14ae6 --- /dev/null +++ b/src/validators/HasWorkPassValidator.ts @@ -0,0 +1,7 @@ +import { Validator, Papers } from '../types'; + +export class HasWorkPassValidator implements Validator { + public validate (papers: Papers): boolean { + return !!papers.getWorkPass() + } +} \ No newline at end of file diff --git a/src/validators/IdConsistencyValidator.ts b/src/validators/IdConsistencyValidator.ts index 70fbd57..90f2e34 100644 --- a/src/validators/IdConsistencyValidator.ts +++ b/src/validators/IdConsistencyValidator.ts @@ -7,7 +7,8 @@ export class IdConsistencyValidator extends ConsistencyValidator