Functional Weave
Code in TypeScript

validation.gtin@1.1.0

impl/typescript.ts

1,730 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { isGs1CheckDigitValid } from "./validation_gs1_check_digit.ts";  ← from validation.gs1-check-digit ^1.0.0 · built alongside by fune
import { type GtinCheck } from "./validation_gtin_types.ts";

/** The four GTIN lengths, and what each is called. */
const KINDS: { readonly [length: number]: string } = { 8: "ean-8", 12: "upc-a", 13: "ean-13", 14: "gtin-14" };

function invalid(reason: string): GtinCheck {
  return { valid: false, normalised: null, kind: null, gtin14: null, reason };
}

/**
 * Check an EAN-8, UPC-A, EAN-13 or GTIN-14 against the GS1 mod-10 check digit.
 *
 * A pass means the number was typed correctly, not that GS1 issued it.
 * Validators answer rather than throw: a malformed value is the answer "no".
 */
export function validateGtin(value: string): GtinCheck {
  if (typeof value !== "string") return invalid("empty");

  // Barcodes are printed in groups, so spaces and hyphens are ignored anywhere.
  const chars: string[] = [];
  for (const ch of value) {
    if (ch !== " " && ch !== "-") chars.push(ch);
  }
  if (chars.length === 0) return invalid("empty");
  for (const ch of chars) {
    if (ch < "0" || ch > "9") return invalid("bad-character");
  }
  const digits = chars.join("");
  const kind = KINDS[digits.length];
  if (kind === undefined) return invalid("bad-length");
  // Zero everywhere satisfies the arithmetic and has never been allocated.
  if (/^0+$/.test(digits)) return invalid("all-zero");

  // The GS1 mod-10 check, weighted from the right, which is what makes one
  // rule serve all four lengths (EAN-13's first digit gets 1, EAN-8's and
  // UPC-A's get 3).
  if (!isGs1CheckDigitValid(digits)) return invalid("bad-check-digit");

  return { valid: true, normalised: digits, kind, gtin14: digits.padStart(14, "0"), reason: null };
}