Functional Weave
Code in Python

validation.vin@1.0.0

README.md

2,965 bytes · view raw

# validation.vin

Checks a vehicle identification number: the 17-character code stamped on the
chassis and printed on a V5C logbook. ISO 3779 fixes the length and alphabet;
the check digit in position 9 comes from the North American standard (US
49 CFR 565.15) and is the part that catches typos.

THE CHECK DIGIT IS NOT UNIVERSAL, and that decides the shape of the answer.
North American law requires it, so a VIN whose world manufacturer identifier
starts with 1 to 5 (United States, Canada, Mexico) and whose check digit does
not match is `valid: false` with `bad-check-digit`. ISO 3779 itself does not
require one, and many European and Japanese manufacturers put a letter or an
arbitrary digit in position 9 for vehicles built for their home markets.
Rejecting those would reject real cars on UK roads, so for every other region a
mismatch leaves `valid: true` and reports `checkDigitMatches: false`. A caller
who knows the vehicle should carry a check digit (anything built for sale in
North America, and most Chinese VINs) can insist on `checkDigitMatches`.

THE CALCULATION: each character becomes a number (digits are themselves;
A-H are 1-8, J-N 1-5, P 7, R 9, S-Z 2-9, skipping the forbidden letters),
is multiplied by the weight for its position
(8 7 6 5 4 3 2 10 0 9 8 7 6 5 4 3 2), and the sum is taken modulo 11; a
remainder of 10 is written `X`. Position 9, the check digit itself, has
weight 0.

FORBIDDEN LETTERS: I, O and Q never appear in a VIN because they read as 1, 0
and 0. They get their own reason, `forbidden-letter`, because they almost
always mean a transcription slip that the user can fix by retyping that one
character.

ACCEPTED INPUT: letters in either case (folded to upper case) and digits;
ASCII spaces are ignored anywhere. Hyphens and anything else are refused,
because VINs are never printed with them.

| reason             | meaning                                                  |
|--------------------|----------------------------------------------------------|
| `empty`            | nothing but spaces, or not a string at all               |
| `bad-character`    | anything other than A-Z, a-z, 0-9 and spaces             |
| `bad-length`       | not exactly 17 characters                                |
| `forbidden-letter` | contains I, O or Q                                       |
| `bad-check-digit`  | a North American VIN (starts 1-5) whose check digit is wrong |

The checks run in that order; the first failure is the reason. Validators
answer rather than throw.

Out of scope: decoding the model year (position 10), the plant, or the
manufacturer's name from the WMI, and pre-1981 VINs, which were not 17
characters.

Sources: ISO 3779:2009 "Road vehicles - Vehicle identification number (VIN) -
Content and structure"; US 49 CFR 565.15 "Content requirements" (check digit,
transliteration and weight tables),
https://www.ecfr.gov/current/title-49/subtitle-B/chapter-V/part-565/subpart-B/section-565.15.