Skip to content

Commit

Permalink
feat: include parsed value in details for invalid text fields (#51)
Browse files Browse the repository at this point in the history
When a value doesn't match the expected regex, it still has a clear value with start and end indexes.

BREAKING CHANGE: The result's `details` may now include a non-null `value` even when it's invalid.
The `start` and `end` fields will refer to it instead of the entire range when that happens.
The `fields` object is unchanged and still contains `null` for invalid fields.

Closes: #50
  • Loading branch information
targos authored Feb 23, 2024
1 parent 5be3275 commit b6e2652
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 19 deletions.
64 changes: 63 additions & 1 deletion src/parse/__tests__/swissDrivingLicense.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('parse Swiss Driving License', () => {
valid: true,
autocorrect: [],
});
expect(result.details[result.details.length - 1]).toStrictEqual({
expect(result.details.at(-1)).toStrictEqual({
label: 'First name',
field: 'firstName',
value: 'FABIENNE',
Expand Down Expand Up @@ -55,6 +55,68 @@ describe('parse Swiss Driving License', () => {
lastName: 'MARCHAND',
});
});
it('invalid text', () => {
const MRZ = [
'AAA001D<<',
'FACHE305142128097<<800126<<<<<',
'M4RCHAND<<FABI3NNE<<<<<<<<<<<<',
];

const result = parse(MRZ);
expect(result.format).toBe('SWISS_DRIVING_LICENSE');
expect(result.valid).toBe(false);
expect(result.details.filter((a) => !a.valid)).toHaveLength(2);
expect(result.details[0]).toStrictEqual({
label: 'Document number',
field: 'documentNumber',
ranges: [{ line: 0, start: 0, end: 9, raw: 'AAA001D<<' }],
line: 0,
start: 0,
end: 7,
value: 'AAA001D',
valid: true,
autocorrect: [],
});
expect(result.details.at(-1)).toStrictEqual({
label: 'First name',
field: 'firstName',
value: 'FABI3NNE',
valid: false,
ranges: [
{
line: 2,
start: 0,
end: 30,
raw: 'M4RCHAND<<FABI3NNE<<<<<<<<<<<<',
},
],
line: 2,
start: 10,
end: 18,
error:
'invalid text: FABI3NNE<<<<<<<<<<<<. Must match the following regular expression: /^[A-Z<]+<*$/',
autocorrect: [],
});
expect(result.details.at(-2)).toMatchObject({
field: 'lastName',
value: 'M4RCHAND',
valid: false,
line: 2,
start: 0,
end: 8,
});
expect(result.fields).toStrictEqual({
documentNumber: 'AAA001D',
languageCode: 'D',
documentCode: 'FA',
issuingState: 'CHE',
pinCode: '305142128',
versionNumber: '097',
birthDate: '800126',
firstName: null,
lastName: null,
});
});
it('Use autocorrect', () => {
const MRZ = [
'AAA001D<<',
Expand Down
10 changes: 8 additions & 2 deletions src/parse/createFieldParser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

import { ParseTextError } from '../parsers/parseText';
import { Autocorrect, Details, FieldName, Range } from '../types';

import { autoCorrection } from './autoCorrection';
Expand Down Expand Up @@ -100,8 +101,13 @@ export default function createFieldParser(
result.start = range.start + parsed.start;
result.end = range.start + parsed.end;
}
} catch (e) {
result.error = e.message;
} catch (err) {
result.error = err.message;
if (err instanceof ParseTextError) {
result.value = err.value;
result.start = range.start + err.start;
result.end = range.start + err.end;
}
}

for (const autocorrectElement of autocorrect) {
Expand Down
12 changes: 7 additions & 5 deletions src/parse/getResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ function getDetails(

function getFields(details: Details[]) {
const fields: FieldRecords = {};
let valid = true;
let allValid = true;
for (const detail of details) {
if (!detail.valid) valid = false;
if (!detail.valid) {
allValid = false;
}
if (detail.field) {
fields[detail.field] = detail.value;
fields[detail.field] = detail.valid ? detail.value : null;
}
}
return { fields, valid };
return { fields, allValid };
}

function getCorrection(
Expand Down Expand Up @@ -71,6 +73,6 @@ export function getResult(
format,
details,
fields: fields.fields,
valid: fields.valid,
valid: fields.allValid,
};
}
7 changes: 5 additions & 2 deletions src/parsers/parseDocumentNumberOptional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ export function parseDocumentNumberOptional(
) {
if (checkDigit === '<') {
const firstFiller = optional.indexOf('<');
const value = parseText(optional.substring(firstFiller + 1));
const value = parseText(
optional.substring(firstFiller + 1),
firstFiller + 1,
);
return {
value,
start: firstFiller + 1,
end: firstFiller + 1 + value.length,
};
} else {
const value = parseText(optional);
const value = parseText(optional, 0);
return {
value,
start: 0,
Expand Down
6 changes: 5 additions & 1 deletion src/parsers/parseFirstName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { parseText } from './parseText';

export default function parseFirstName(source: string) {
const withoutStart = source.replace(/.*?<{2}/, '');
const value = parseText(withoutStart, /^[A-Z<]+<*$/);
const value = parseText(
withoutStart,
source.length - withoutStart.length,
/^[A-Z<]+<*$/,
);
const start = source.length - withoutStart.length;
return {
value,
Expand Down
2 changes: 1 addition & 1 deletion src/parsers/parseLastName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { parseText } from './parseText';

export default function parseLastName(source: string) {
const parsed = parseText(source.replace(/<{2}.*/, ''), /^[A-Z<]*<*$/);
const parsed = parseText(source.replace(/<{2}.*$/, ''), 0, /^[A-Z<]*<*$/);
return {
value: parsed,
start: 0,
Expand Down
4 changes: 2 additions & 2 deletions src/parsers/parseOptional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import { parseText } from './parseText';

export function parseOptional(source: string) {
const value = parseText(source);
const value = parseText(source, 0);

return {
value,
start: 0,
end: 0 + value.length,
end: value.length,
};
}
2 changes: 1 addition & 1 deletion src/parsers/parsePersonalNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { parseText } from './parseText';

export function parsePersonalNumber(source: string) {
const value = parseText(source, /^[A-Z0-9<]+<*$/);
const value = parseText(source, 0, /^[A-Z0-9<]+<*$/);
return {
value,
start: 0,
Expand Down
27 changes: 23 additions & 4 deletions src/parsers/parseText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,30 @@

import { cleanText } from './cleanText';

export function parseText(source: string, regexp = /^[0-9A-Z<]+$/) {
if (!source.match(regexp)) {
throw new Error(
export function parseText(
source: string,
initialStart: number,
regexp = /^[0-9A-Z<]+$/,
) {
const cleaned = cleanText(source);
if (!regexp.test(source)) {
throw new ParseTextError(
`invalid text: ${source}. Must match the following regular expression: ${regexp.toString()}`,
cleaned,
initialStart,
initialStart + cleaned.length,
);
}
return cleanText(source);
return cleaned;
}

export class ParseTextError extends Error {
constructor(
message: string,
public readonly value: string,
public readonly start: number,
public readonly end: number,
) {
super(message);
}
}

0 comments on commit b6e2652

Please sign in to comment.