diff --git a/src/parse/__tests__/swissDrivingLicense.test.ts b/src/parse/__tests__/swissDrivingLicense.test.ts index 71aed36..e0ffbd8 100644 --- a/src/parse/__tests__/swissDrivingLicense.test.ts +++ b/src/parse/__tests__/swissDrivingLicense.test.ts @@ -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', @@ -55,6 +55,68 @@ describe('parse Swiss Driving License', () => { lastName: 'MARCHAND', }); }); + it('invalid text', () => { + const MRZ = [ + 'AAA001D<<', + 'FACHE305142128097<<800126<<<<<', + 'M4RCHAND< !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< { const MRZ = [ 'AAA001D<<', diff --git a/src/parse/createFieldParser.ts b/src/parse/createFieldParser.ts index e3f608a..1c4b9ed 100644 --- a/src/parse/createFieldParser.ts +++ b/src/parse/createFieldParser.ts @@ -1,5 +1,6 @@ 'use strict'; +import { ParseTextError } from '../parsers/parseText'; import { Autocorrect, Details, FieldName, Range } from '../types'; import { autoCorrection } from './autoCorrection'; @@ -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) { diff --git a/src/parse/getResult.ts b/src/parse/getResult.ts index 9a9a1d2..8a8a375 100644 --- a/src/parse/getResult.ts +++ b/src/parse/getResult.ts @@ -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( @@ -71,6 +73,6 @@ export function getResult( format, details, fields: fields.fields, - valid: fields.valid, + valid: fields.allValid, }; } diff --git a/src/parsers/parseDocumentNumberOptional.ts b/src/parsers/parseDocumentNumberOptional.ts index 7ebe674..ad9587a 100644 --- a/src/parsers/parseDocumentNumberOptional.ts +++ b/src/parsers/parseDocumentNumberOptional.ts @@ -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, diff --git a/src/parsers/parseFirstName.ts b/src/parsers/parseFirstName.ts index 72420f5..75ac457 100644 --- a/src/parsers/parseFirstName.ts +++ b/src/parsers/parseFirstName.ts @@ -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, diff --git a/src/parsers/parseLastName.ts b/src/parsers/parseLastName.ts index 70e8b4d..b43c501 100644 --- a/src/parsers/parseLastName.ts +++ b/src/parsers/parseLastName.ts @@ -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, diff --git a/src/parsers/parseOptional.ts b/src/parsers/parseOptional.ts index 72516e2..92fe198 100644 --- a/src/parsers/parseOptional.ts +++ b/src/parsers/parseOptional.ts @@ -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, }; } diff --git a/src/parsers/parsePersonalNumber.ts b/src/parsers/parsePersonalNumber.ts index 5c624f5..9dc779d 100644 --- a/src/parsers/parsePersonalNumber.ts +++ b/src/parsers/parsePersonalNumber.ts @@ -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, diff --git a/src/parsers/parseText.ts b/src/parsers/parseText.ts index 12687d0..994925e 100644 --- a/src/parsers/parseText.ts +++ b/src/parsers/parseText.ts @@ -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); + } }